diff options
842 files changed, 19939 insertions, 6748 deletions
diff --git a/.gitignore b/.gitignore index c47cc8bf2538..5018436798d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .idea *.iml *.sw* -gen/
\ No newline at end of file +gen/ +.vscode/ +*.code-workspace diff --git a/Android.bp b/Android.bp index 35f97ac57281..5c0dd63ac0f7 100644 --- a/Android.bp +++ b/Android.bp @@ -532,6 +532,7 @@ java_library { "android.hardware.vibrator-V1.2-java", "android.hardware.vibrator-V1.3-java", "android.security.apc-java", + "android.security.authorization-java", "android.system.keystore2-java", "android.system.suspend.control.internal-java", "cameraprotosnano", diff --git a/apct-tests/perftests/OWNERS b/apct-tests/perftests/OWNERS index a060ad9a5a7e..7e7feafdc5a5 100644 --- a/apct-tests/perftests/OWNERS +++ b/apct-tests/perftests/OWNERS @@ -1,2 +1,11 @@ -timmurray@google.com +balejs@google.com +carmenjackson@google.com +cfijalkovich@google.com +dualli@google.com +edgararriaga@google.com +jpakaravoor@google.com +kevinjeon@google.com philipcuadra@google.com +shombert@google.com +timmurray@google.com +wessam@google.com diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt index 9fa4b8e5a4e7..2320b75247a0 100644 --- a/apex/appsearch/framework/api/current.txt +++ b/apex/appsearch/framework/api/current.txt @@ -8,6 +8,7 @@ package android.app.appsearch { } public class AppSearchManager { + method public void createGlobalSearchSession(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.GlobalSearchSession>>); method public void createSearchSession(@NonNull android.app.appsearch.AppSearchManager.SearchContext, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.AppSearchSession>>); } @@ -80,7 +81,8 @@ package android.app.appsearch { method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setTokenizerType(int); } - public final class AppSearchSession { + public final class AppSearchSession implements java.io.Closeable { + method public void close(); method public void getByUri(@NonNull android.app.appsearch.GetByUriRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,android.app.appsearch.GenericDocument>); method public void getSchema(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.util.Set<android.app.appsearch.AppSearchSchema>>>); method public void putDocuments(@NonNull android.app.appsearch.PutDocumentsRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>); @@ -149,6 +151,11 @@ package android.app.appsearch { method @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String); } + public class GlobalSearchSession implements java.io.Closeable { + method public void close(); + method @NonNull public android.app.appsearch.SearchResults query(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor); + } + public class PackageIdentifier { ctor public PackageIdentifier(@NonNull String, @NonNull byte[]); method @NonNull public String getPackageName(); diff --git a/apex/appsearch/framework/api/system-current.txt b/apex/appsearch/framework/api/system-current.txt index 73a4a196b90a..4a6194e2915d 100644 --- a/apex/appsearch/framework/api/system-current.txt +++ b/apex/appsearch/framework/api/system-current.txt @@ -1,17 +1,9 @@ // Signature format: 2.0 package android.app.appsearch { - public class AppSearchManager { - method public void createGlobalSearchSession(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.GlobalSearchSession>>); - } - public class AppSearchManagerFrameworkInitializer { method public static void initialize(); } - public class GlobalSearchSession { - method @NonNull public android.app.appsearch.SearchResults query(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor); - } - } diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java index a5b7080fcfba..6fa8f850100b 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java @@ -17,7 +17,6 @@ package android.app.appsearch; import android.annotation.CallbackExecutor; import android.annotation.NonNull; -import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; import android.os.Bundle; @@ -123,8 +122,8 @@ public class AppSearchManager { /** * Creates a new {@link AppSearchSession}. * - * <p>This process requires an AppSearch native indexing file system for each user. If it's not - * created for this user, the initialization process will create one under user's directory. + * <p>This process requires an AppSearch native indexing file system. If it's not created, the + * initialization process will create one under the user's credential encrypted directory. * * @param searchContext The {@link SearchContext} contains all information to create a new * {@link AppSearchSession} @@ -147,16 +146,14 @@ public class AppSearchManager { /** * Creates a new {@link GlobalSearchSession}. * - * <p>This process requires an AppSearch native indexing file system for each user. If it's not - * created for this user, the initialization process will create one under user's directory. + * <p>This process requires an AppSearch native indexing file system. If it's not created, the + * initialization process will create one under the user's credential encrypted directory. * * @param executor Executor on which to invoke the callback. * @param callback The {@link AppSearchResult}<{@link GlobalSearchSession}> of * performing this operation. Or a {@link AppSearchResult} with failure * reason code and error information. - * @hide */ - @SystemApi public void createGlobalSearchSession( @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<AppSearchResult<GlobalSearchSession>> callback) { diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java index d4872e815134..042757797dce 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java @@ -28,6 +28,7 @@ import android.util.Log; import com.android.internal.util.Preconditions; +import java.io.Closeable; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -42,7 +43,7 @@ import java.util.function.Consumer; * * This class is thread safe. */ -public final class AppSearchSession { +public final class AppSearchSession implements Closeable { private static final String TAG = "AppSearchSession"; private final String mDatabaseName; @UserIdInt @@ -490,11 +491,10 @@ public final class AppSearchSession { } /** - * Closes the SearchSessionImpl to persists all update/delete requests to the disk. - * - * @hide + * Closes the {@link AppSearchSession} to persist all schema and document updates, additions, + * and deletes to disk. */ - // TODO(b/175637134) when unhide it, implement Closeable and remove this method. + @Override public void close() { if (mIsMutated && !mIsClosed) { try { diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java index 95f7d796d007..e4e030e0d18a 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java @@ -19,10 +19,12 @@ package android.app.appsearch; import android.annotation.CallbackExecutor; import android.annotation.NonNull; -import android.annotation.SystemApi; import android.annotation.UserIdInt; import android.os.RemoteException; +import com.android.internal.util.Preconditions; + +import java.io.Closeable; import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -31,14 +33,13 @@ import java.util.function.Consumer; * This class provides global access to the centralized AppSearch index maintained by the system. * * <p>Apps can retrieve indexed documents through the query API. - * @hide */ -@SystemApi -public class GlobalSearchSession { +public class GlobalSearchSession implements Closeable { private final IAppSearchManager mService; @UserIdInt private final int mUserId; + private boolean mIsClosed = false; static void createGlobalSearchSession( @NonNull IAppSearchManager service, @@ -129,7 +130,14 @@ public class GlobalSearchSession { Objects.requireNonNull(queryExpression); Objects.requireNonNull(searchSpec); Objects.requireNonNull(executor); + Preconditions.checkState(!mIsClosed, "GlobalSearchSession has already been closed"); return new SearchResults(mService, /*databaseName=*/null, queryExpression, searchSpec, mUserId, executor); } + + /** Closes the {@link GlobalSearchSession}. */ + @Override + public void close() { + mIsClosed = true; + } } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java index 4931cc026466..62324b2d93f7 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java @@ -52,6 +52,9 @@ public final class SearchResult { /** @hide */ public static final String PACKAGE_NAME_FIELD = "packageName"; + /** @hide */ + public static final String DATABASE_NAME_FIELD = "databaseName"; + @NonNull private final Bundle mBundle; /** Cache of the inflated document. Comes from inflating mDocumentBundle at first use. */ @@ -119,6 +122,17 @@ public final class SearchResult { } /** + * Contains the database name that stored the {@link GenericDocument}. + * + * @return Database name that stored the document + * @hide + */ + @NonNull + public String getDatabaseName() { + return Preconditions.checkNotNull(mBundle.getString(DATABASE_NAME_FIELD)); + } + + /** * This class represents a match objects for any Snippets that might be present in {@link * SearchResults} from query. Using this class user can get the full text, exact matches and * Snippets of document content for a given match. 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 b75492629a2a..674f199f0022 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 @@ -45,6 +45,7 @@ import com.google.android.icing.proto.DocumentProto; import com.google.android.icing.proto.GetAllNamespacesResultProto; import com.google.android.icing.proto.GetOptimizeInfoResultProto; import com.google.android.icing.proto.GetResultProto; +import com.google.android.icing.proto.GetResultSpecProto; import com.google.android.icing.proto.GetSchemaResultProto; import com.google.android.icing.proto.IcingSearchEngineOptions; import com.google.android.icing.proto.InitializeResultProto; @@ -62,6 +63,7 @@ import com.google.android.icing.proto.SearchResultProto; import com.google.android.icing.proto.SearchSpecProto; import com.google.android.icing.proto.SetSchemaResultProto; import com.google.android.icing.proto.StatusProto; +import com.google.android.icing.proto.TypePropertyMask; import java.io.File; import java.util.ArrayList; @@ -432,7 +434,9 @@ public final class AppSearchImpl { try { getResultProto = mIcingSearchEngineLocked.get( - createPrefix(packageName, databaseName) + namespace, uri); + createPrefix(packageName, databaseName) + namespace, + uri, + GetResultSpecProto.getDefaultInstance()); } finally { mReadWriteLock.readLock().unlock(); } @@ -691,8 +695,6 @@ public final class AppSearchImpl { * <p>If the app crashes before a call to PersistToDisk(), Icing would trigger a costly recovery * process in next initialization. After that, Icing would still be able to recover all written * data. - * - * @throws AppSearchException */ public void persistToDisk() throws AppSearchException { PersistToDiskResultProto persistToDiskResultProto = @@ -986,13 +988,12 @@ public final class AppSearchImpl { return false; } - List<ResultSpecProto.TypePropertyMask> prefixedTypePropertyMasks = new ArrayList<>(); + List<TypePropertyMask> prefixedTypePropertyMasks = new ArrayList<>(); // Rewrite filters to include a database prefix. for (String prefix : existingPrefixes) { Set<String> existingSchemaTypes = mSchemaMapLocked.get(prefix); // Qualify the given schema types - for (ResultSpecProto.TypePropertyMask typePropertyMask : - resultSpecBuilder.getTypePropertyMasksList()) { + for (TypePropertyMask typePropertyMask : resultSpecBuilder.getTypePropertyMasksList()) { String qualifiedType = prefix + typePropertyMask.getSchemaType(); if (existingSchemaTypes.contains(qualifiedType)) { prefixedTypePropertyMasks.add( @@ -1060,12 +1061,36 @@ public final class AppSearchImpl { int delimiterIndex = prefix.indexOf(PACKAGE_DELIMITER); if (delimiterIndex == -1) { // This should never happen if we construct our prefixes properly - Log.wtf(TAG, "Malformed prefix doesn't contain package name: " + prefix); + Log.wtf(TAG, "Malformed prefix doesn't contain package delimiter: " + prefix); return ""; } return prefix.substring(0, delimiterIndex); } + /** + * Returns the database name that's contained within the {@code prefix}. + * + * @param prefix Prefix string that contains the database name inside of it. The database name + * must be between the {@link #PACKAGE_DELIMITER} and {@link #DATABASE_DELIMITER} + * @return Valid database name. + */ + @NonNull + private static String getDatabaseName(@NonNull String prefix) { + int packageDelimiterIndex = prefix.indexOf(PACKAGE_DELIMITER); + int databaseDelimiterIndex = prefix.indexOf(DATABASE_DELIMITER); + if (packageDelimiterIndex == -1) { + // This should never happen if we construct our prefixes properly + Log.wtf(TAG, "Malformed prefix doesn't contain package delimiter: " + prefix); + return ""; + } + if (databaseDelimiterIndex == -1) { + // This should never happen if we construct our prefixes properly + Log.wtf(TAG, "Malformed prefix doesn't contain database delimiter: " + prefix); + return ""; + } + return prefix.substring(packageDelimiterIndex + 1, databaseDelimiterIndex); + } + @NonNull private static String removePrefix(@NonNull String prefixedString) throws AppSearchException { // The prefix is made up of the package, then the database. So we only need to find the @@ -1178,6 +1203,9 @@ public final class AppSearchImpl { // Parallel array of package names for each document search result. List<String> packageNames = new ArrayList<>(searchResultProto.getResultsCount()); + // Parallel array of database names for each document search result. + List<String> databaseNames = new ArrayList<>(searchResultProto.getResultsCount()); + SearchResultProto.Builder resultsBuilder = searchResultProto.toBuilder(); for (int i = 0; i < searchResultProto.getResultsCount(); i++) { SearchResultProto.ResultProto.Builder resultBuilder = @@ -1185,10 +1213,12 @@ public final class AppSearchImpl { DocumentProto.Builder documentBuilder = resultBuilder.getDocument().toBuilder(); String prefix = removePrefixesFromDocument(documentBuilder); packageNames.add(getPackageName(prefix)); + databaseNames.add(getDatabaseName(prefix)); resultBuilder.setDocument(documentBuilder); resultsBuilder.setResults(i, resultBuilder); } - return SearchResultToProtoConverter.toSearchResultPage(resultsBuilder, packageNames); + return SearchResultToProtoConverter.toSearchResultPage( + resultsBuilder, packageNames, databaseNames); } @GuardedBy("mReadWriteLock") diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java index ccd567d1c945..f422ebc2db7b 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java @@ -46,11 +46,16 @@ public class SearchResultToProtoConverter { * @param proto The {@link SearchResultProto} containing results. * @param packageNames A parallel array of package names. The package name at index 'i' of this * list should be the package that indexed the document at index 'i' of proto.getResults(i). + * @param databaseNames A parallel array of database names. The database name at index 'i' of + * this list shold be the database that indexed the document at index 'i' of + * proto.getResults(i). * @return {@link SearchResultPage} of results. */ @NonNull public static SearchResultPage toSearchResultPage( - @NonNull SearchResultProtoOrBuilder proto, @NonNull List<String> packageNames) { + @NonNull SearchResultProtoOrBuilder proto, + @NonNull List<String> packageNames, + @NonNull List<String> databaseNames) { Preconditions.checkArgument( proto.getResultsCount() == packageNames.size(), "Size of " + "results does not match the number of package names."); @@ -58,7 +63,9 @@ public class SearchResultToProtoConverter { bundle.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, proto.getNextPageToken()); ArrayList<Bundle> resultBundles = new ArrayList<>(proto.getResultsCount()); for (int i = 0; i < proto.getResultsCount(); i++) { - resultBundles.add(toSearchResultBundle(proto.getResults(i), packageNames.get(i))); + resultBundles.add( + toSearchResultBundle( + proto.getResults(i), packageNames.get(i), databaseNames.get(i))); } bundle.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles); return new SearchResultPage(bundle); @@ -69,16 +76,20 @@ public class SearchResultToProtoConverter { * * @param proto The proto to be converted. * @param packageName The package name associated with the document in {@code proto}. + * @param databaseName The database name associated with the document in {@code proto}. * @return A {@link SearchResult} bundle. */ @NonNull private static Bundle toSearchResultBundle( - @NonNull SearchResultProto.ResultProtoOrBuilder proto, @NonNull String packageName) { + @NonNull SearchResultProto.ResultProtoOrBuilder proto, + @NonNull String packageName, + @NonNull String databaseName) { Bundle bundle = new Bundle(); GenericDocument document = GenericDocumentToProtoConverter.toGenericDocument(proto.getDocument()); bundle.putBundle(SearchResult.DOCUMENT_FIELD, document.getBundle()); bundle.putString(SearchResult.PACKAGE_NAME_FIELD, packageName); + bundle.putString(SearchResult.DATABASE_NAME_FIELD, databaseName); ArrayList<Bundle> matchList = new ArrayList<>(); if (proto.hasSnippet()) { diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java index 073a7f624d89..0d7d3e12aa0e 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java @@ -25,6 +25,7 @@ import com.google.android.icing.proto.ResultSpecProto; import com.google.android.icing.proto.ScoringSpecProto; import com.google.android.icing.proto.SearchSpecProto; import com.google.android.icing.proto.TermMatchType; +import com.google.android.icing.proto.TypePropertyMask; import java.util.List; import java.util.Map; @@ -71,7 +72,7 @@ public final class SearchSpecToProtoConverter { Map<String, List<String>> projectionTypePropertyPaths = spec.getProjections(); for (Map.Entry<String, List<String>> e : projectionTypePropertyPaths.entrySet()) { builder.addTypePropertyMasks( - ResultSpecProto.TypePropertyMask.newBuilder() + TypePropertyMask.newBuilder() .setSchemaType(e.getKey()) .addAllPaths(e.getValue())); } @@ -107,8 +108,7 @@ public final class SearchSpecToProtoConverter { case SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP: return ScoringSpecProto.RankingStrategy.Code.CREATION_TIMESTAMP; case SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE: - return ScoringSpecProto.RankingStrategy.Code - .RELEVANCE_SCORE_NONFUNCTIONAL_PLACEHOLDER; + return ScoringSpecProto.RankingStrategy.Code.RELEVANCE_SCORE; default: throw new IllegalArgumentException( "Invalid result ranking strategy: " + rankingStrategyCode); diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt index f8e5a8aba17b..9be30495eb90 100644 --- a/apex/appsearch/synced_jetpack_changeid.txt +++ b/apex/appsearch/synced_jetpack_changeid.txt @@ -1 +1 @@ -I6745091e5cb97d69ce2e5f85d3d15c073e7e3ef7 +Icd58005ad659b6b3d03f683f8954939175324685 diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java index 6859747286a8..39ca6871ba09 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java @@ -62,6 +62,7 @@ public class GlobalSearchSessionShimImpl implements GlobalSearchSessionShim { @NonNull GlobalSearchSession session, @NonNull ExecutorService executor) { mGlobalSearchSession = Preconditions.checkNotNull(session); mExecutor = Preconditions.checkNotNull(executor); + } @NonNull @@ -72,4 +73,9 @@ public class GlobalSearchSessionShimImpl implements GlobalSearchSessionShim { mGlobalSearchSession.query(queryExpression, searchSpec, mExecutor); return new SearchResultsShimImpl(searchResults, mExecutor); } + + @Override + public void close() { + mGlobalSearchSession.close(); + } } diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java index e439c5ab3947..3e819687dae0 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java @@ -21,6 +21,7 @@ import android.annotation.SuppressLint; import com.google.common.util.concurrent.ListenableFuture; +import java.io.Closeable; import java.util.Set; /** @@ -29,7 +30,7 @@ import java.util.Set; * * <p>All implementations of this interface must be thread safe. */ -public interface AppSearchSessionShim { +public interface AppSearchSessionShim extends Closeable { /** * Sets the schema that will be used by documents provided to the {@link #putDocuments} method. @@ -207,11 +208,9 @@ public interface AppSearchSessionShim { @NonNull String queryExpression, @NonNull SearchSpec searchSpec); /** - * Closes the SearchSessionImpl to persists all update/delete requests to the disk. - * - * @hide + * Closes the {@link AppSearchSessionShim} to persist all schema and document updates, + * additions, and deletes to disk. */ - - // TODO(b/175637134) when unhide it, extends Closeable and remove this method. + @Override void close(); } diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java index 2d09247dfbc9..cd867a47d407 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java @@ -18,12 +18,14 @@ package android.app.appsearch; import android.annotation.NonNull; +import java.io.Closeable; + /** * This class provides global access to the centralized AppSearch index maintained by the system. * * <p>Apps can retrieve indexed documents through the query API. */ -public interface GlobalSearchSessionShim { +public interface GlobalSearchSessionShim extends Closeable { /** * Searches across all documents in the storage based on a given query string. * @@ -65,4 +67,8 @@ public interface GlobalSearchSessionShim { */ @NonNull SearchResultsShim query(@NonNull String queryExpression, @NonNull SearchSpec searchSpec); + + /** Closes the {@link GlobalSearchSessionShim}. */ + @Override + void close(); } diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java index 18643ed91276..444164390550 100644 --- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java @@ -16,6 +16,8 @@ package com.android.server; +import android.app.BroadcastOptions; + import com.android.server.deviceidle.IDeviceIdleConstraint; public interface DeviceIdleInternal { @@ -32,8 +34,17 @@ public interface DeviceIdleInternal { void addPowerSaveTempWhitelistApp(int callingUid, String packageName, long duration, int userId, boolean sync, String reason); - // duration in milliseconds - void addPowerSaveTempWhitelistAppDirect(int uid, long duration, boolean sync, + /** + * Called by ActivityManagerService to directly add UID to DeviceIdleController's temp + * allowlist. + * @param uid + * @param duration duration in milliseconds + * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType} + * @param sync + * @param reason + */ + void addPowerSaveTempWhitelistAppDirect(int uid, long duration, + @BroadcastOptions.TempAllowListType int type, boolean sync, String reason); // duration in milliseconds diff --git a/apex/jobscheduler/framework/java/com/android/server/PowerAllowlistInternal.java b/apex/jobscheduler/framework/java/com/android/server/PowerAllowlistInternal.java new file mode 100644 index 000000000000..b5d1838b2277 --- /dev/null +++ b/apex/jobscheduler/framework/java/com/android/server/PowerAllowlistInternal.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.annotation.NonNull; + +public interface PowerAllowlistInternal { + /** + * Listener to be notified when the temporary allowlist changes. + */ + interface TempAllowlistChangeListener { + void onAppAdded(int uid); + void onAppRemoved(int uid); + } + + /** + * Registers a listener that will be notified when the temp allowlist changes. + */ + void registerTempAllowlistChangeListener(@NonNull TempAllowlistChangeListener listener); + + /** + * Unregisters a registered stationary listener from being notified when the temp allowlist + * changes. + */ + void unregisterTempAllowlistChangeListener(@NonNull TempAllowlistChangeListener listener); +} diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index 99892527721a..7aed32c16ae2 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -17,9 +17,12 @@ package com.android.server; import android.Manifest; +import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AlarmManager; +import android.app.BroadcastOptions; +import android.app.BroadcastOptions.TempAllowListType; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -558,6 +561,9 @@ public class DeviceIdleController extends SystemService private final ArraySet<DeviceIdleInternal.StationaryListener> mStationaryListeners = new ArraySet<>(); + private final ArraySet<PowerAllowlistInternal.TempAllowlistChangeListener> + mTempAllowlistChangeListeners = new ArraySet<>(); + private static final int EVENT_NULL = 0; private static final int EVENT_NORMAL = 1; private static final int EVENT_LIGHT_IDLE = 2; @@ -741,6 +747,20 @@ public class DeviceIdleController extends SystemService } } + private void registerTempAllowlistChangeListener( + @NonNull PowerAllowlistInternal.TempAllowlistChangeListener listener) { + synchronized (this) { + mTempAllowlistChangeListeners.add(listener); + } + } + + private void unregisterTempAllowlistChangeListener( + @NonNull PowerAllowlistInternal.TempAllowlistChangeListener listener) { + synchronized (this) { + mTempAllowlistChangeListeners.remove(listener); + } + } + @VisibleForTesting final class MotionListener extends TriggerEventListener implements SensorEventListener { @@ -1508,12 +1528,13 @@ public class DeviceIdleController extends SystemService @VisibleForTesting static final int MSG_REPORT_STATIONARY_STATUS = 7; private static final int MSG_FINISH_IDLE_OP = 8; - private static final int MSG_REPORT_TEMP_APP_WHITELIST_CHANGED = 9; + private static final int MSG_REPORT_TEMP_APP_WHITELIST_CHANGED_TO_NPMS = 9; private static final int MSG_SEND_CONSTRAINT_MONITORING = 10; @VisibleForTesting static final int MSG_UPDATE_PRE_IDLE_TIMEOUT_FACTOR = 11; @VisibleForTesting static final int MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR = 12; + private static final int MSG_REPORT_TEMP_APP_WHITELIST_CHANGED = 13; final class MyHandler extends Handler { MyHandler(Looper looper) { @@ -1606,14 +1627,31 @@ public class DeviceIdleController extends SystemService } break; case MSG_TEMP_APP_WHITELIST_TIMEOUT: { // TODO: What is keeping the device awake at this point? Does it need to be? - int appId = msg.arg1; - checkTempAppWhitelistTimeout(appId); + int uid = msg.arg1; + checkTempAppWhitelistTimeout(uid); } break; case MSG_FINISH_IDLE_OP: { // mActiveIdleWakeLock is held at this point decActiveIdleOps(); } break; case MSG_REPORT_TEMP_APP_WHITELIST_CHANGED: { + final int uid = msg.arg1; + final boolean added = (msg.arg2 == 1); + PowerAllowlistInternal.TempAllowlistChangeListener[] listeners; + synchronized (DeviceIdleController.this) { + listeners = mTempAllowlistChangeListeners.toArray( + new PowerAllowlistInternal.TempAllowlistChangeListener[ + mTempAllowlistChangeListeners.size()]); + } + for (PowerAllowlistInternal.TempAllowlistChangeListener listener : listeners) { + if (added) { + listener.onAppAdded(uid); + } else { + listener.onAppRemoved(uid); + } + } + } break; + case MSG_REPORT_TEMP_APP_WHITELIST_CHANGED_TO_NPMS: { final int appId = msg.arg1; final boolean added = (msg.arg2 == 1); mNetworkPolicyManagerInternal.onTempPowerSaveWhitelistChange(appId, added); @@ -1905,9 +1943,9 @@ public class DeviceIdleController extends SystemService // duration in milliseconds @Override - public void addPowerSaveTempWhitelistAppDirect(int uid, long duration, boolean sync, - String reason) { - addPowerSaveTempWhitelistAppDirectInternal(0, uid, duration, sync, reason); + public void addPowerSaveTempWhitelistAppDirect(int uid, long duration, + @TempAllowListType int type, boolean sync, String reason) { + addPowerSaveTempWhitelistAppDirectInternal(0, uid, duration, type, sync, reason); } // duration in milliseconds @@ -1960,6 +1998,21 @@ public class DeviceIdleController extends SystemService } } + private class LocalPowerAllowlistService implements PowerAllowlistInternal { + + @Override + public void registerTempAllowlistChangeListener( + @NonNull TempAllowlistChangeListener listener) { + DeviceIdleController.this.registerTempAllowlistChangeListener(listener); + } + + @Override + public void unregisterTempAllowlistChangeListener( + @NonNull TempAllowlistChangeListener listener) { + DeviceIdleController.this.unregisterTempAllowlistChangeListener(listener); + } + } + static class Injector { private final Context mContext; private ConnectivityManager mConnectivityManager; @@ -2164,6 +2217,7 @@ public class DeviceIdleController extends SystemService publishBinderService(Context.DEVICE_IDLE_CONTROLLER, mBinderService); mLocalService = new LocalService(); publishLocalService(DeviceIdleInternal.class, mLocalService); + publishLocalService(PowerAllowlistInternal.class, new LocalPowerAllowlistService()); } @Override @@ -2667,7 +2721,9 @@ public class DeviceIdleController extends SystemService long duration, int userId, boolean sync, String reason) { try { int uid = getContext().getPackageManager().getPackageUidAsUser(packageName, userId); - addPowerSaveTempWhitelistAppDirectInternal(callingUid, uid, duration, sync, reason); + addPowerSaveTempWhitelistAppDirectInternal(callingUid, uid, duration, + BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED, sync, + reason); } catch (NameNotFoundException e) { } } @@ -2677,7 +2733,7 @@ public class DeviceIdleController extends SystemService * app an exemption to access network and acquire wakelocks. */ void addPowerSaveTempWhitelistAppDirectInternal(int callingUid, int uid, - long duration, boolean sync, String reason) { + long duration, @TempAllowListType int type, boolean sync, String reason) { final long timeNow = SystemClock.elapsedRealtime(); boolean informWhitelistChanged = false; int appId = UserHandle.getAppId(uid); @@ -2708,15 +2764,18 @@ public class DeviceIdleController extends SystemService reason, uid); } catch (RemoteException e) { } - postTempActiveTimeoutMessage(appId, duration); - updateTempWhitelistAppIdsLocked(appId, true); + postTempActiveTimeoutMessage(uid, duration); + updateTempWhitelistAppIdsLocked(uid, true, duration, type); if (sync) { informWhitelistChanged = true; } else { - mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED, appId, 1) + // NPMS needs to update its state synchronously in certain situations so we + // can't have it use the TempAllowlistChangeListener path right now. + // TODO: see if there's a way to simplify/consolidate + mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED_TO_NPMS, appId, 1) .sendToTarget(); } - reportTempWhitelistChangedLocked(); + reportTempWhitelistChangedLocked(uid, true); } } if (informWhitelistChanged) { @@ -2731,13 +2790,13 @@ public class DeviceIdleController extends SystemService try { final int uid = getContext().getPackageManager().getPackageUidAsUser( packageName, userId); - final int appId = UserHandle.getAppId(uid); - removePowerSaveTempWhitelistAppDirectInternal(appId); + removePowerSaveTempWhitelistAppDirectInternal(uid); } catch (NameNotFoundException e) { } } - private void removePowerSaveTempWhitelistAppDirectInternal(int appId) { + private void removePowerSaveTempWhitelistAppDirectInternal(int uid) { + final int appId = UserHandle.getAppId(uid); synchronized (this) { final int idx = mTempWhitelistAppIdEndTimes.indexOfKey(appId); if (idx < 0) { @@ -2746,51 +2805,54 @@ public class DeviceIdleController extends SystemService } final String reason = mTempWhitelistAppIdEndTimes.valueAt(idx).second; mTempWhitelistAppIdEndTimes.removeAt(idx); - onAppRemovedFromTempWhitelistLocked(appId, reason); + onAppRemovedFromTempWhitelistLocked(uid, reason); } } - private void postTempActiveTimeoutMessage(int appId, long delay) { + private void postTempActiveTimeoutMessage(int uid, long delay) { if (DEBUG) { - Slog.d(TAG, "postTempActiveTimeoutMessage: appId=" + appId + ", delay=" + delay); + Slog.d(TAG, "postTempActiveTimeoutMessage: uid=" + uid + ", delay=" + delay); } mHandler.sendMessageDelayed( - mHandler.obtainMessage(MSG_TEMP_APP_WHITELIST_TIMEOUT, appId, 0), delay); + mHandler.obtainMessage(MSG_TEMP_APP_WHITELIST_TIMEOUT, uid, 0), delay); } - void checkTempAppWhitelistTimeout(int appId) { + void checkTempAppWhitelistTimeout(int uid) { final long timeNow = SystemClock.elapsedRealtime(); + final int appId = UserHandle.getAppId(uid); if (DEBUG) { - Slog.d(TAG, "checkTempAppWhitelistTimeout: appId=" + appId + ", timeNow=" + timeNow); + Slog.d(TAG, "checkTempAppWhitelistTimeout: uid=" + uid + ", timeNow=" + timeNow); } synchronized (this) { - Pair<MutableLong, String> entry = mTempWhitelistAppIdEndTimes.get(appId); + Pair<MutableLong, String> entry = + mTempWhitelistAppIdEndTimes.get(appId); if (entry == null) { // Nothing to do return; } if (timeNow >= entry.first.value) { mTempWhitelistAppIdEndTimes.delete(appId); - onAppRemovedFromTempWhitelistLocked(appId, entry.second); + onAppRemovedFromTempWhitelistLocked(uid, entry.second); } else { // Need more time if (DEBUG) { - Slog.d(TAG, "Time to remove AppId " + appId + ": " + entry.first.value); + Slog.d(TAG, "Time to remove uid " + uid + ": " + entry.first.value); } - postTempActiveTimeoutMessage(appId, entry.first.value - timeNow); + postTempActiveTimeoutMessage(uid, entry.first.value - timeNow); } } } @GuardedBy("this") - private void onAppRemovedFromTempWhitelistLocked(int appId, String reason) { + private void onAppRemovedFromTempWhitelistLocked(int uid, String reason) { if (DEBUG) { - Slog.d(TAG, "Removing appId " + appId + " from temp whitelist"); + Slog.d(TAG, "Removing uid " + uid + " from temp whitelist"); } - updateTempWhitelistAppIdsLocked(appId, false); + final int appId = UserHandle.getAppId(uid); + updateTempWhitelistAppIdsLocked(uid, false, 0, 0); mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED, appId, 0) .sendToTarget(); - reportTempWhitelistChangedLocked(); + reportTempWhitelistChangedLocked(uid, false); try { mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_TEMP_WHITELIST_FINISH, reason, appId); @@ -3811,7 +3873,16 @@ public class DeviceIdleController extends SystemService passWhiteListsToForceAppStandbyTrackerLocked(); } - private void updateTempWhitelistAppIdsLocked(int appId, boolean adding) { + /** + * update temp allowlist. + * @param uid uid to add or remove from temp allowlist. + * @param adding true to add to temp allowlist, false to remove from temp allowlist. + * @param durationMs duration in milliseconds to add to temp allowlist, only valid when + * param adding is true. + * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType} + */ + private void updateTempWhitelistAppIdsLocked(int uid, boolean adding, long durationMs, + @TempAllowListType int type) { final int size = mTempWhitelistAppIdEndTimes.size(); if (mTempWhitelistAppIdArray.length != size) { mTempWhitelistAppIdArray = new int[size]; @@ -3824,8 +3895,8 @@ public class DeviceIdleController extends SystemService Slog.d(TAG, "Setting activity manager temp whitelist to " + Arrays.toString(mTempWhitelistAppIdArray)); } - mLocalActivityManager.updateDeviceIdleTempWhitelist(mTempWhitelistAppIdArray, appId, - adding); + mLocalActivityManager.updateDeviceIdleTempWhitelist(mTempWhitelistAppIdArray, uid, + adding, durationMs, type); } if (mLocalPowerManager != null) { if (DEBUG) { @@ -3843,7 +3914,9 @@ public class DeviceIdleController extends SystemService getContext().sendBroadcastAsUser(intent, UserHandle.SYSTEM); } - private void reportTempWhitelistChangedLocked() { + private void reportTempWhitelistChangedLocked(final int uid, final boolean added) { + mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED, uid, added ? 1 : 0) + .sendToTarget(); Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); getContext().sendBroadcastAsUser(intent, UserHandle.SYSTEM); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index a9ca7308b25f..26b5abed745c 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -260,11 +260,9 @@ public final class JobServiceContext implements ServiceConnection { try { final int bindFlags; if (job.shouldTreatAsExpeditedJob()) { - // Add BIND_FOREGROUND_SERVICE to make it BFGS. Without it, it'll be - // PROCESS_STATE_IMPORTANT_FOREGROUND. Unclear which is better here. // TODO(171305774): The job should run on the little cores. We'll probably need // another binding flag for that. - bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE; + bindFlags = Context.BIND_AUTO_CREATE; } else { bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_PERCEPTIBLE; diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index a02f8de5e292..736ee18b5444 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -60,6 +60,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseArrayMap; import android.util.SparseBooleanArray; +import android.util.SparseLongArray; import android.util.SparseSetArray; import android.util.proto.ProtoOutputStream; @@ -68,6 +69,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.server.JobSchedulerBackgroundThread; import com.android.server.LocalServices; +import com.android.server.PowerAllowlistInternal; import com.android.server.job.ConstantsProto; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobServiceContext; @@ -343,6 +345,15 @@ public final class QuotaController extends StateController { */ private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>(); + /** Current set of UIDs on the temp allowlist. */ + private final SparseBooleanArray mTempAllowlistCache = new SparseBooleanArray(); + + /** + * Mapping of app IDs to the when their temp allowlist grace period ends (in the elapsed + * realtime timebase). + */ + private final SparseLongArray mTempAllowlistGraceCache = new SparseLongArray(); + private final ActivityManagerInternal mActivityManagerInternal; private final AlarmManager mAlarmManager; private final ChargingTracker mChargeTracker; @@ -538,6 +549,9 @@ public final class QuotaController extends StateController { */ private long mEJRewardNotificationSeenMs = QcConstants.DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS; + private long mEJTempAllowlistGracePeriodMs = + QcConstants.DEFAULT_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS; + /** The package verifier app. */ @Nullable private String mPackageVerifier; @@ -562,6 +576,9 @@ public final class QuotaController extends StateController { * userId will the first arg. */ private static final int MSG_PROCESS_USAGE_EVENT = 5; + /** A UID's free quota grace period has ended. */ + @VisibleForTesting + static final int MSG_END_GRACE_PERIOD = 6; public QuotaController(@NonNull JobSchedulerService service, @NonNull BackgroundJobsController backgroundJobsController, @@ -586,6 +603,9 @@ public final class QuotaController extends StateController { UsageStatsManagerInternal usmi = LocalServices.getService(UsageStatsManagerInternal.class); usmi.registerListener(new UsageEventTracker()); + PowerAllowlistInternal pai = LocalServices.getService(PowerAllowlistInternal.class); + pai.registerTempAllowlistChangeListener(new TempAllowlistTracker()); + try { ActivityManager.getService().registerUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE, @@ -693,6 +713,8 @@ public final class QuotaController extends StateController { clearAppStatsLocked(UserHandle.getUserId(uid), packageName); mForegroundUids.delete(uid); mUidToPackageCache.remove(uid); + mTempAllowlistCache.delete(uid); + mTempAllowlistGraceCache.delete(uid); } @Override @@ -1988,10 +2010,15 @@ public final class QuotaController extends StateController { } private boolean shouldTrackLocked() { + final long nowElapsed = sElapsedRealtimeClock.millis(); final int standbyBucket = JobSchedulerService.standbyBucketForPackage(mPkg.packageName, - mPkg.userId, sElapsedRealtimeClock.millis()); + mPkg.userId, nowElapsed); + final long tempAllowlistGracePeriodEndElapsed = mTempAllowlistGraceCache.get(mUid); + final boolean hasTempAllowlistExemption = !mRegularJobTimer + && (mTempAllowlistCache.get(mUid) + || nowElapsed < tempAllowlistGracePeriodEndElapsed); return (standbyBucket == RESTRICTED_INDEX || !mChargeTracker.isCharging()) - && !mForegroundUids.get(mUid); + && !mForegroundUids.get(mUid) && !hasTempAllowlistExemption; } void onStateChangedLocked(long nowElapsed, boolean isQuotaFree) { @@ -2265,6 +2292,38 @@ public final class QuotaController extends StateController { } } + final class TempAllowlistTracker implements PowerAllowlistInternal.TempAllowlistChangeListener { + + @Override + public void onAppAdded(int uid) { + final long nowElapsed = sElapsedRealtimeClock.millis(); + mTempAllowlistCache.put(uid, true); + final ArraySet<String> packages = getPackagesForUid(uid); + if (packages != null) { + final int userId = UserHandle.getUserId(uid); + for (int i = packages.size() - 1; i >= 0; --i) { + Timer t = mEJPkgTimers.get(userId, packages.valueAt(i)); + if (t != null) { + t.onStateChangedLocked(nowElapsed, true); + } + } + if (maybeUpdateConstraintForUidLocked(uid)) { + mStateChangedListener.onControllerStateChanged(); + } + } + } + + @Override + public void onAppRemoved(int uid) { + final long nowElapsed = sElapsedRealtimeClock.millis(); + final long endElapsed = nowElapsed + mEJTempAllowlistGracePeriodMs; + mTempAllowlistCache.delete(uid); + mTempAllowlistGraceCache.put(uid, endElapsed); + Message msg = mHandler.obtainMessage(MSG_END_GRACE_PERIOD, uid, 0); + mHandler.sendMessageDelayed(msg, mEJTempAllowlistGracePeriodMs); + } + } + private final class DeleteTimingSessionsFunctor implements Consumer<List<TimingSession>> { private final Predicate<TimingSession> mTooOld = new Predicate<TimingSession>() { public boolean test(TimingSession ts) { @@ -2291,6 +2350,26 @@ public final class QuotaController extends StateController { // getRemainingEJExecutionTimeLocked(). } + @Nullable + private ArraySet<String> getPackagesForUid(final int uid) { + ArraySet<String> packages = mUidToPackageCache.get(uid); + if (packages == null) { + try { + String[] pkgs = AppGlobals.getPackageManager() + .getPackagesForUid(uid); + if (pkgs != null) { + for (String pkg : pkgs) { + mUidToPackageCache.add(uid, pkg); + } + packages = mUidToPackageCache.get(uid); + } + } catch (RemoteException e) { + // Shouldn't happen. + } + } + return packages; + } + private class QcHandler extends Handler { private boolean mIsProcessing; @@ -2396,21 +2475,7 @@ public final class QuotaController extends StateController { // Update Timers first. if (mPkgTimers.indexOfKey(userId) >= 0 || mEJPkgTimers.indexOfKey(userId) >= 0) { - ArraySet<String> packages = mUidToPackageCache.get(uid); - if (packages == null) { - try { - String[] pkgs = AppGlobals.getPackageManager() - .getPackagesForUid(uid); - if (pkgs != null) { - for (String pkg : pkgs) { - mUidToPackageCache.add(uid, pkg); - } - packages = mUidToPackageCache.get(uid); - } - } catch (RemoteException e) { - Slog.wtf(TAG, "Failed to get package list", e); - } - } + final ArraySet<String> packages = getPackagesForUid(uid); if (packages != null) { for (int i = packages.size() - 1; i >= 0; --i) { Timer t = mEJPkgTimers.get(userId, packages.valueAt(i)); @@ -2466,6 +2531,37 @@ public final class QuotaController extends StateController { break; } + case MSG_END_GRACE_PERIOD: { + final int uid = msg.arg1; + synchronized (mLock) { + if (mTempAllowlistCache.get(uid)) { + // App added back to the temp allowlist during the grace period. + if (DEBUG) { + Slog.d(TAG, uid + " is still allowed"); + } + break; + } + if (DEBUG) { + Slog.d(TAG, uid + " is now out of grace period"); + } + final ArraySet<String> packages = getPackagesForUid(uid); + if (packages != null) { + final int userId = UserHandle.getUserId(uid); + final long nowElapsed = sElapsedRealtimeClock.millis(); + for (int i = packages.size() - 1; i >= 0; --i) { + Timer t = mEJPkgTimers.get(userId, packages.valueAt(i)); + if (t != null) { + t.onStateChangedLocked(nowElapsed, false); + } + } + if (maybeUpdateConstraintForUidLocked(uid)) { + mStateChangedListener.onControllerStateChanged(); + } + } + } + + break; + } } } @@ -2784,6 +2880,9 @@ public final class QuotaController extends StateController { @VisibleForTesting static final String KEY_EJ_REWARD_NOTIFICATION_SEEN_MS = QC_CONSTANT_PREFIX + "ej_reward_notification_seen_ms"; + @VisibleForTesting + static final String KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS = + QC_CONSTANT_PREFIX + "ej_temp_allowlist_grace_period_ms"; private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_MS = 10 * 60 * 1000L; // 10 minutes @@ -2836,6 +2935,7 @@ public final class QuotaController extends StateController { private static final long DEFAULT_EJ_REWARD_TOP_APP_MS = 10 * SECOND_IN_MILLIS; private static final long DEFAULT_EJ_REWARD_INTERACTION_MS = 15 * SECOND_IN_MILLIS; private static final long DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS = 0; + private static final long DEFAULT_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS = 3 * MINUTE_IN_MILLIS; /** How much time each app will have to run jobs within their standby bucket window. */ public long ALLOWED_TIME_PER_PERIOD_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_MS; @@ -3063,6 +3163,12 @@ public final class QuotaController extends StateController { */ public long EJ_REWARD_NOTIFICATION_SEEN_MS = DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS; + /** + * How much additional grace period to add to the end of an app's temp allowlist + * duration. + */ + public long EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS = DEFAULT_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS; + public void processConstantLocked(@NonNull DeviceConfig.Properties properties, @NonNull String key) { switch (key) { @@ -3245,22 +3351,25 @@ public final class QuotaController extends StateController { properties.getLong(key, DEFAULT_EJ_REWARD_INTERACTION_MS); // Limit interaction reward to be in the range [5 seconds, 15 minutes] per // event. - long newInteractionReward = Math.min(15 * MINUTE_IN_MILLIS, + mEJRewardInteractionMs = Math.min(15 * MINUTE_IN_MILLIS, Math.max(5 * SECOND_IN_MILLIS, EJ_REWARD_INTERACTION_MS)); - if (mEJRewardInteractionMs != newInteractionReward) { - mEJRewardInteractionMs = newInteractionReward; - } break; case KEY_EJ_REWARD_NOTIFICATION_SEEN_MS: // We don't need to re-evaluate execution stats or constraint status for this. EJ_REWARD_NOTIFICATION_SEEN_MS = properties.getLong(key, DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS); // Limit notification seen reward to be in the range [0, 5] minutes per event. - long newNotiSeenReward = Math.min(5 * MINUTE_IN_MILLIS, + mEJRewardNotificationSeenMs = Math.min(5 * MINUTE_IN_MILLIS, Math.max(0, EJ_REWARD_NOTIFICATION_SEEN_MS)); - if (mEJRewardNotificationSeenMs != newNotiSeenReward) { - mEJRewardNotificationSeenMs = newNotiSeenReward; - } + break; + case KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS: + // We don't need to re-evaluate execution stats or constraint status for this. + EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS = + properties.getLong(key, DEFAULT_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS); + // Limit grace period to be in the range [0 minutes, 1 hour]. + mEJTempAllowlistGracePeriodMs = Math.min(HOUR_IN_MILLIS, + Math.max(0, EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS)); + break; } } @@ -3522,6 +3631,8 @@ public final class QuotaController extends StateController { pw.print(KEY_EJ_REWARD_TOP_APP_MS, EJ_REWARD_TOP_APP_MS).println(); pw.print(KEY_EJ_REWARD_INTERACTION_MS, EJ_REWARD_INTERACTION_MS).println(); pw.print(KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, EJ_REWARD_NOTIFICATION_SEEN_MS).println(); + pw.print(KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, + EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS).println(); pw.decreaseIndent(); } @@ -3668,6 +3779,10 @@ public final class QuotaController extends StateController { return mEJRewardTopAppMs; } + @VisibleForTesting + long getEJTempAllowlistGracePeriodMs() { + return mEJTempAllowlistGracePeriodMs; + } @VisibleForTesting @Nullable @@ -3757,6 +3872,12 @@ public final class QuotaController extends StateController { pw.decreaseIndent(); pw.println(); + pw.print("Cached temp allowlist: "); + pw.println(mTempAllowlistCache.toString()); + pw.print("Cached temp allowlist grace period: "); + pw.println(mTempAllowlistGraceCache.toString()); + + pw.println(); mTrackedJobs.forEach((jobs) -> { for (int j = 0; j < jobs.size(); j++) { final JobStatus js = jobs.valueAt(j); diff --git a/apex/media/framework/api/current.txt b/apex/media/framework/api/current.txt index ce3bcbede4c7..103bb47fca23 100644 --- a/apex/media/framework/api/current.txt +++ b/apex/media/framework/api/current.txt @@ -6,9 +6,11 @@ package android.media { method public int describeContents(); method @NonNull public java.util.List<java.lang.String> getSupportedHdrTypes(); method @NonNull public java.util.List<java.lang.String> getSupportedVideoMimeTypes(); - method public boolean isHdrTypeSupported(@NonNull String); + method @NonNull public java.util.List<java.lang.String> getUnsupportedHdrTypes(); + method @NonNull public java.util.List<java.lang.String> getUnsupportedVideoMimeTypes(); + method public boolean isHdrTypeSupported(@NonNull String) throws android.media.ApplicationMediaCapabilities.FormatNotFoundException; method public boolean isSlowMotionSupported(); - method public boolean isVideoMimeTypeSupported(@NonNull String); + method public boolean isVideoMimeTypeSupported(@NonNull String) throws android.media.ApplicationMediaCapabilities.FormatNotFoundException; method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.media.ApplicationMediaCapabilities> CREATOR; } @@ -17,10 +19,16 @@ package android.media { ctor public ApplicationMediaCapabilities.Builder(); method @NonNull public android.media.ApplicationMediaCapabilities.Builder addSupportedHdrType(@NonNull String); method @NonNull public android.media.ApplicationMediaCapabilities.Builder addSupportedVideoMimeType(@NonNull String); + method @NonNull public android.media.ApplicationMediaCapabilities.Builder addUnsupportedHdrType(@NonNull String); + method @NonNull public android.media.ApplicationMediaCapabilities.Builder addUnsupportedVideoMimeType(@NonNull String); method @NonNull public android.media.ApplicationMediaCapabilities build(); method @NonNull public android.media.ApplicationMediaCapabilities.Builder setSlowMotionSupported(boolean); } + public static class ApplicationMediaCapabilities.FormatNotFoundException extends android.util.AndroidException { + ctor public ApplicationMediaCapabilities.FormatNotFoundException(@NonNull String); + } + public class MediaController2 implements java.lang.AutoCloseable { method public void cancelSessionCommand(@NonNull Object); method public void close(); diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java index 36f6b94b16ad..25ccec2930f1 100644 --- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java +++ b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java @@ -22,6 +22,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.util.AndroidException; import android.util.Log; import org.xmlpull.v1.XmlPullParser; @@ -68,35 +69,73 @@ import java.util.Set; public final class ApplicationMediaCapabilities implements Parcelable { private static final String TAG = "ApplicationMediaCapabilities"; + /** + * This exception is thrown when a given format is not specified in the media capabilities. + */ + public static class FormatNotFoundException extends AndroidException { + public FormatNotFoundException(@NonNull String format) { + super(format); + } + } + /** List of supported video codec mime types. */ // TODO: init it with avc and mpeg4 as application is assuming to support them. private Set<String> mSupportedVideoMimeTypes = new HashSet<>(); + /** List of unsupported video codec mime types. */ + private Set<String> mUnsupportedVideoMimeTypes = new HashSet<>(); + /** List of supported hdr types. */ private Set<String> mSupportedHdrTypes = new HashSet<>(); + /** List of unsupported hdr types. */ + private Set<String> mUnsupportedHdrTypes = new HashSet<>(); + private boolean mIsSlowMotionSupported = false; private ApplicationMediaCapabilities(Builder b) { mSupportedVideoMimeTypes.addAll(b.getSupportedVideoMimeTypes()); + mUnsupportedVideoMimeTypes.addAll(b.getUnsupportedVideoMimeTypes()); mSupportedHdrTypes.addAll(b.getSupportedHdrTypes()); + mUnsupportedHdrTypes.addAll(b.getUnsupportedHdrTypes()); mIsSlowMotionSupported = b.mIsSlowMotionSupported; } /** - * Query if an video codec is supported by the application. + * Query if a video codec format is supported by the application. + * @param videoMime The mime type of the video codec format. Must be the one used in + * {@link MediaFormat#KEY_MIME}. + * @return true if application supports the video codec format, false otherwise. + * @throws FormatNotFoundException if the application did not specify the codec either in the + * supported or unsupported formats. */ public boolean isVideoMimeTypeSupported( - @NonNull String videoMime) { - return mSupportedVideoMimeTypes.contains(videoMime); + @NonNull String videoMime) throws FormatNotFoundException { + if (mUnsupportedVideoMimeTypes.contains(videoMime)) { + return false; + } else if (mSupportedVideoMimeTypes.contains(videoMime)) { + return true; + } else { + throw new FormatNotFoundException(videoMime); + } } /** - * Query if a hdr type is supported by the application. + * Query if a HDR type is supported by the application. + * @param hdrType The type of the HDR format. + * @return true if application supports the HDR format, false otherwise. + * @throws FormatNotFoundException if the application did not specify the format either in the + * supported or unsupported formats. */ public boolean isHdrTypeSupported( - @NonNull @MediaFeature.MediaHdrType String hdrType) { - return mSupportedHdrTypes.contains(hdrType); + @NonNull @MediaFeature.MediaHdrType String hdrType) throws FormatNotFoundException { + if (mUnsupportedHdrTypes.contains(hdrType)) { + return false; + } else if (mSupportedHdrTypes.contains(hdrType)) { + return true; + } else { + throw new FormatNotFoundException(hdrType); + } } @Override @@ -111,11 +150,21 @@ public final class ApplicationMediaCapabilities implements Parcelable { for (String cap : mSupportedVideoMimeTypes) { dest.writeString(cap); } + // Write out the unsupported video mime types. + dest.writeInt(mUnsupportedVideoMimeTypes.size()); + for (String cap : mUnsupportedVideoMimeTypes) { + dest.writeString(cap); + } // Write out the supported hdr types. dest.writeInt(mSupportedHdrTypes.size()); for (String cap : mSupportedHdrTypes) { dest.writeString(cap); } + // Write out the unsupported hdr types. + dest.writeInt(mUnsupportedHdrTypes.size()); + for (String cap : mUnsupportedHdrTypes) { + dest.writeString(cap); + } // Write out the supported slow motion. dest.writeBoolean(mIsSlowMotionSupported); } @@ -124,7 +173,9 @@ public final class ApplicationMediaCapabilities implements Parcelable { public String toString() { String caps = new String( "Supported Video MimeTypes: " + mSupportedVideoMimeTypes.toString()); + caps += "Unsupported Video MimeTypes: " + mUnsupportedVideoMimeTypes.toString(); caps += "Supported HDR types: " + mSupportedHdrTypes.toString(); + caps += "Unsupported HDR types: " + mUnsupportedHdrTypes.toString(); caps += "Supported slow motion: " + mIsSlowMotionSupported; return caps; } @@ -159,9 +210,8 @@ public final class ApplicationMediaCapabilities implements Parcelable { }; /* - * Returns a list that contains all the video codec mime types supported by the application. - * The list will be empty if no codecs are supported by the application. - * @return List of supported video codec mime types. + * Query the video codec mime types supported by the application. + * @return List of supported video codec mime types. The list will be empty if there are none. */ @NonNull public List<String> getSupportedVideoMimeTypes() { @@ -169,9 +219,17 @@ public final class ApplicationMediaCapabilities implements Parcelable { } /* - * Returns a list that contains all hdr types supported by the application. - * The list will be empty if no hdr types are supported by the application. - * @return List of supported hdr types. + * Query the video codec mime types that are not supported by the application. + * @return List of unsupported video codec mime types. The list will be empty if there are none. + */ + @NonNull + public List<String> getUnsupportedVideoMimeTypes() { + return new ArrayList<>(mSupportedVideoMimeTypes); + } + + /* + * Query all hdr types that are supported by the application. + * @return List of supported hdr types. The list will be empty if there are none. */ @NonNull public List<String> getSupportedHdrTypes() { @@ -179,6 +237,15 @@ public final class ApplicationMediaCapabilities implements Parcelable { } /* + * Query all hdr types that are not supported by the application. + * @return List of unsupported hdr types. The list will be empty if there are none. + */ + @NonNull + public List<String> getUnsupportedHdrTypes() { + return new ArrayList<>(mUnsupportedHdrTypes); + } + + /* * Whether handling of slow-motion video is supported */ public boolean isSlowMotionSupported() { @@ -213,6 +280,12 @@ public final class ApplicationMediaCapabilities implements Parcelable { /** List of supported hdr types. */ private Set<String> mSupportedHdrTypes = new HashSet<>(); + /** List of unsupported video codec mime types. */ + private Set<String> mUnsupportedVideoMimeTypes = new HashSet<>(); + + /** List of unsupported hdr types. */ + private Set<String> mUnsupportedHdrTypes = new HashSet<>(); + private boolean mIsSlowMotionSupported = false; /* Map to save the format read from the xml. */ @@ -299,26 +372,50 @@ public final class ApplicationMediaCapabilities implements Parcelable { case "HEVC": if (isSupported) { mSupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_HEVC); + } else { + mUnsupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_HEVC); + } + break; + case "VP9": + if (isSupported) { + mSupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_VP9); + } else { + mUnsupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_VP9); + } + break; + case "AV1": + if (isSupported) { + mSupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_AV1); + } else { + mUnsupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_AV1); } break; case "HDR10": if (isSupported) { mSupportedHdrTypes.add(MediaFeature.HdrType.HDR10); + } else { + mUnsupportedHdrTypes.add(MediaFeature.HdrType.HDR10); } break; case "HDR10Plus": if (isSupported) { mSupportedHdrTypes.add(MediaFeature.HdrType.HDR10_PLUS); + } else { + mUnsupportedHdrTypes.add(MediaFeature.HdrType.HDR10_PLUS); } break; case "Dolby-Vision": if (isSupported) { mSupportedHdrTypes.add(MediaFeature.HdrType.DOLBY_VISION); + } else { + mUnsupportedHdrTypes.add(MediaFeature.HdrType.DOLBY_VISION); } break; case "HLG": if (isSupported) { mSupportedHdrTypes.add(MediaFeature.HdrType.HLG); + } else { + mUnsupportedHdrTypes.add(MediaFeature.HdrType.HLG); } break; case "SlowMotion": @@ -348,8 +445,11 @@ public final class ApplicationMediaCapabilities implements Parcelable { @NonNull public ApplicationMediaCapabilities build() { Log.d(TAG, - "Building ApplicationMediaCapabilities with: " + mSupportedHdrTypes.toString() - + " " + mSupportedVideoMimeTypes.toString() + " " + "Building ApplicationMediaCapabilities with: (Supported HDR: " + + mSupportedHdrTypes.toString() + " Unsupported HDR: " + + mUnsupportedHdrTypes.toString() + ") (Supported Codec: " + + " " + mSupportedVideoMimeTypes.toString() + " Unsupported Codec:" + + mUnsupportedVideoMimeTypes.toString() + ") " + mIsSlowMotionSupported); // If hdr is supported, application must also support hevc. @@ -365,8 +465,7 @@ public final class ApplicationMediaCapabilities implements Parcelable { * * @param codecMime Supported codec mime types. Must be one of the mime type defined * in {@link MediaFormat}. - * @throws UnsupportedOperationException if the codec mime type is not supported. - * @throws IllegalArgumentException if mime type is not valid. + * @throws IllegalArgumentException if mime type is not valid. */ @NonNull public Builder addSupportedVideoMimeType( @@ -379,16 +478,49 @@ public final class ApplicationMediaCapabilities implements Parcelable { return new ArrayList<>(mSupportedVideoMimeTypes); } + private boolean isValidVideoCodecMimeType(@NonNull String codecMime) { + if (!codecMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC) + && !codecMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP9) + && !codecMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AV1)) { + return false; + } + return true; + } + + /** + * Adds an unsupported video codec mime type. + * + * @param codecMime Unsupported codec mime type. Must be one of the mime type defined + * in {@link MediaFormat}. + * @throws IllegalArgumentException if mime type is not valid. + */ + @NonNull + public Builder addUnsupportedVideoMimeType( + @NonNull String codecMime) { + if (!isValidVideoCodecMimeType(codecMime)) { + throw new IllegalArgumentException("Invalid codec mime type: " + codecMime); + } + mUnsupportedVideoMimeTypes.add(codecMime); + return this; + } + + private List<String> getUnsupportedVideoMimeTypes() { + return new ArrayList<>(mUnsupportedVideoMimeTypes); + } + /** * Adds a supported hdr type. * - * @param hdrType Supported hdr types. Must be one of the String defined in + * @param hdrType Supported hdr type. Must be one of the String defined in * {@link MediaFeature.HdrType}. * @throws IllegalArgumentException if hdrType is not valid. */ @NonNull public Builder addSupportedHdrType( @NonNull @MediaFeature.MediaHdrType String hdrType) { + if (!isValidVideoCodecHdrType(hdrType)) { + throw new IllegalArgumentException("Invalid hdr type: " + hdrType); + } mSupportedHdrTypes.add(hdrType); return this; } @@ -397,6 +529,37 @@ public final class ApplicationMediaCapabilities implements Parcelable { return new ArrayList<>(mSupportedHdrTypes); } + private boolean isValidVideoCodecHdrType(@NonNull String hdrType) { + if (!hdrType.equals(MediaFeature.HdrType.DOLBY_VISION) + && !hdrType.equals(MediaFeature.HdrType.HDR10) + && !hdrType.equals(MediaFeature.HdrType.HDR10_PLUS) + && !hdrType.equals(MediaFeature.HdrType.HLG)) { + return false; + } + return true; + } + + /** + * Adds an unsupported hdr type. + * + * @param hdrType Unsupported hdr type. Must be one of the String defined in + * {@link MediaFeature.HdrType}. + * @throws IllegalArgumentException if hdrType is not valid. + */ + @NonNull + public Builder addUnsupportedHdrType( + @NonNull @MediaFeature.MediaHdrType String hdrType) { + if (!isValidVideoCodecHdrType(hdrType)) { + throw new IllegalArgumentException("Invalid hdr type: " + hdrType); + } + mUnsupportedHdrTypes.add(hdrType); + return this; + } + + private List<String> getUnsupportedHdrTypes() { + return new ArrayList<>(mUnsupportedHdrTypes); + } + /** * Sets whether slow-motion video is supported. * If an application indicates support for slow-motion, it is application's responsibility diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java index 3d706e40bc0b..55c462973c53 100644 --- a/apex/media/framework/java/android/media/MediaTranscodeManager.java +++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java @@ -980,8 +980,15 @@ public final class MediaTranscodeManager { throw new UnsupportedOperationException( "Source video format hint must be set!"); } - boolean supportHevc = mClientCaps.isVideoMimeTypeSupported( - MediaFormat.MIMETYPE_VIDEO_HEVC); + + boolean supportHevc = false; + try { + supportHevc = mClientCaps.isVideoMimeTypeSupported( + MediaFormat.MIMETYPE_VIDEO_HEVC); + } catch (ApplicationMediaCapabilities.FormatNotFoundException ex) { + // Set to false if application did not specify. + supportHevc = false; + } if (!supportHevc && MediaFormat.MIMETYPE_VIDEO_HEVC.equals( mSrcVideoFormatHint.getString(MediaFormat.KEY_MIME))) { return true; @@ -1016,13 +1023,11 @@ public final class MediaTranscodeManager { "Source Width and height must be larger than 0"); } - // TODO(hkuang): Remove the hardcoded frameRate after b/176940364 is fixed. - float frameRate = (float) 30.0; - /*mSrcVideoFormatHint.getFloat(MediaFormat.KEY_FRAME_RATE, frameRate); + float frameRate = mSrcVideoFormatHint.getFloat(MediaFormat.KEY_FRAME_RATE); if (frameRate <= 0) { throw new IllegalArgumentException( "frameRate must be larger than 0"); - }*/ + } int bitrate = getAVCBitrate(width, height, frameRate); videoTrackFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java index ef1e413a3112..9088db8c66a1 100644 --- a/cmds/sm/src/com/android/commands/sm/Sm.java +++ b/cmds/sm/src/com/android/commands/sm/Sm.java @@ -105,6 +105,8 @@ public final class Sm { runStartCheckpoint(); } else if ("supports-checkpoint".equals(op)) { runSupportsCheckpoint(); + } else if ("unmount-app-data-dirs".equals(op)) { + runDisableAppDataIsolation(); } else { throw new IllegalArgumentException(); } @@ -251,6 +253,13 @@ public final class Sm { System.out.println(result.get()); } + public void runDisableAppDataIsolation() throws RemoteException { + final String pkgName = nextArg(); + final int pid = Integer.parseInt(nextArg()); + final int userId = Integer.parseInt(nextArg()); + mSm.disableAppDataIsolation(pkgName, pid, userId); + } + public void runForget() throws RemoteException { final String fsUuid = nextArg(); if ("all".equals(fsUuid)) { @@ -347,6 +356,8 @@ public final class Sm { System.err.println(""); System.err.println(" sm supports-checkpoint"); System.err.println(""); + System.err.println(" sm unmount-app-data-dirs PACKAGE_NAME PID USER_ID"); + System.err.println(""); return 1; } } diff --git a/core/api/current.txt b/core/api/current.txt index bfa69dc46e28..ba49b6758748 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -6760,7 +6760,7 @@ package android.app { method public android.content.Intent getCropAndSetWallpaperIntent(android.net.Uri); method public int getDesiredMinimumHeight(); method public int getDesiredMinimumWidth(); - method public android.graphics.drawable.Drawable getDrawable(); + method @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public android.graphics.drawable.Drawable getDrawable(); method @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public android.graphics.drawable.Drawable getFastDrawable(); method public static android.app.WallpaperManager getInstance(android.content.Context); method @Nullable public android.app.WallpaperColors getWallpaperColors(int); @@ -8163,6 +8163,7 @@ package android.app.usage { method public long getAppBytes(); method public long getCacheBytes(); method public long getDataBytes(); + method public long getExternalCacheBytes(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.StorageStats> CREATOR; } @@ -10350,6 +10351,7 @@ package android.content { field public static final String BIOMETRIC_SERVICE = "biometric"; field public static final String BLOB_STORE_SERVICE = "blob_store"; field public static final String BLUETOOTH_SERVICE = "bluetooth"; + field public static final String BUGREPORT_SERVICE = "bugreport"; field public static final String CAMERA_SERVICE = "camera"; field public static final String CAPTIONING_SERVICE = "captioning"; field public static final String CARRIER_CONFIG_SERVICE = "carrier_config"; @@ -10837,6 +10839,8 @@ package android.content { field public static final String ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED"; field public static final String ACTION_POWER_USAGE_SUMMARY = "android.intent.action.POWER_USAGE_SUMMARY"; field public static final String ACTION_PROCESS_TEXT = "android.intent.action.PROCESS_TEXT"; + field public static final String ACTION_PROFILE_ACCESSIBLE = "android.intent.action.PROFILE_ACCESSIBLE"; + field public static final String ACTION_PROFILE_INACCESSIBLE = "android.intent.action.PROFILE_INACCESSIBLE"; field public static final String ACTION_PROVIDER_CHANGED = "android.intent.action.PROVIDER_CHANGED"; field public static final String ACTION_QUICK_CLOCK = "android.intent.action.QUICK_CLOCK"; field public static final String ACTION_QUICK_VIEW = "android.intent.action.QUICK_VIEW"; @@ -12049,7 +12053,6 @@ package android.content.pm { public static class PackageInstaller.Session implements java.io.Closeable { method public void abandon(); - method @Deprecated public void addChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>) throws java.io.IOException; method public void addChildSessionId(int); method public void close(); method public void commit(@NonNull android.content.IntentSender); @@ -12063,6 +12066,7 @@ package android.content.pm { method @NonNull public java.io.OutputStream openWrite(@NonNull String, long, long) throws java.io.IOException; method public void removeChildSessionId(int); method public void removeSplit(@NonNull String) throws java.io.IOException; + method @Deprecated public void setChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>, @Nullable byte[]) throws java.io.IOException; method public void setStagingProgress(float); method public void transfer(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; } @@ -15543,7 +15547,6 @@ package android.graphics { ctor public Point(int, int); ctor public Point(@NonNull android.graphics.Point); method public int describeContents(); - method public final void dump(@NonNull java.io.PrintWriter); method public final boolean equals(int, int); method public final void negate(); method public final void offset(int, int); @@ -30082,6 +30085,24 @@ package android.os { method public boolean unlinkToDeath(@NonNull android.os.IBinder.DeathRecipient, int); } + public final class BugreportManager { + method public void cancelBugreport(); + method public void startConnectivityBugreport(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback); + } + + public abstract static class BugreportManager.BugreportCallback { + ctor public BugreportManager.BugreportCallback(); + method public void onEarlyReportFinished(); + method public void onError(int); + method public void onFinished(); + method public void onProgress(@FloatRange(from=0.0f, to=100.0f) float); + field public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5; // 0x5 + field public static final int BUGREPORT_ERROR_INVALID_INPUT = 1; // 0x1 + field public static final int BUGREPORT_ERROR_RUNTIME = 2; // 0x2 + field public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4; // 0x4 + field public static final int BUGREPORT_ERROR_USER_DENIED_CONSENT = 3; // 0x3 + } + public class Build { ctor public Build(); method @NonNull public static java.util.List<android.os.Build.Partition> getFingerprintedPartitions(); @@ -34471,6 +34492,7 @@ package android.provider { field public static final String ACTION_LOCATION_SOURCE_SETTINGS = "android.settings.LOCATION_SOURCE_SETTINGS"; field public static final String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS"; field public static final String ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION = "android.settings.MANAGE_ALL_FILES_ACCESS_PERMISSION"; + field public static final String ACTION_MANAGE_ALL_SUBSCRIPTIONS_SETTINGS = "android.settings.MANAGE_ALL_SUBSCRIPTIONS_SETTINGS"; field public static final String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS"; field public static final String ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION = "android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION"; field public static final String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS"; @@ -39764,6 +39786,7 @@ package android.telephony { field public static final int BAND_25 = 25; // 0x19 field public static final int BAND_257 = 257; // 0x101 field public static final int BAND_258 = 258; // 0x102 + field public static final int BAND_26 = 26; // 0x1a field public static final int BAND_260 = 260; // 0x104 field public static final int BAND_261 = 261; // 0x105 field public static final int BAND_28 = 28; // 0x1c @@ -39775,10 +39798,12 @@ package android.telephony { field public static final int BAND_39 = 39; // 0x27 field public static final int BAND_40 = 40; // 0x28 field public static final int BAND_41 = 41; // 0x29 + field public static final int BAND_46 = 46; // 0x2e field public static final int BAND_48 = 48; // 0x30 field public static final int BAND_5 = 5; // 0x5 field public static final int BAND_50 = 50; // 0x32 field public static final int BAND_51 = 51; // 0x33 + field public static final int BAND_53 = 53; // 0x35 field public static final int BAND_65 = 65; // 0x41 field public static final int BAND_66 = 66; // 0x42 field public static final int BAND_7 = 7; // 0x7 @@ -39804,6 +39829,7 @@ package android.telephony { field public static final int BAND_93 = 93; // 0x5d field public static final int BAND_94 = 94; // 0x5e field public static final int BAND_95 = 95; // 0x5f + field public static final int BAND_96 = 96; // 0x60 } public static final class AccessNetworkConstants.UtranBand { @@ -41315,17 +41341,27 @@ package android.telephony { public final class PhysicalChannelConfig implements android.os.Parcelable { method public int describeContents(); - method public int getCellBandwidthDownlink(); - method public int getChannelNumber(); + method @IntRange(from=1, to=261) public int getBand(); + method @IntRange(from=1) public int getCellBandwidthDownlinkKhz(); + method @IntRange(from=1) public int getCellBandwidthUplinkKhz(); + method @Deprecated public int getChannelNumber(); method public int getConnectionStatus(); + method @IntRange(from=0) public int getDownlinkChannelNumber(); + method @IntRange(from=0) public int getDownlinkFrequencyKhz(); method public int getNetworkType(); method @IntRange(from=0, to=1007) public int getPhysicalCellId(); + method @IntRange(from=0) public int getUplinkChannelNumber(); + method @IntRange(from=0) public int getUplinkFrequencyKhz(); method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int BAND_UNKNOWN = 0; // 0x0 + field public static final int CELL_BANDWIDTH_UNKNOWN = 0; // 0x0 field public static final int CHANNEL_NUMBER_UNKNOWN = -1; // 0xffffffff field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1 field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2 field public static final int CONNECTION_UNKNOWN = -1; // 0xffffffff field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhysicalChannelConfig> CREATOR; + field public static final int FREQUENCY_UNKNOWN = -1; // 0xffffffff + field public static final int PHYSICAL_CELL_ID_MAXIMUM_VALUE = 1007; // 0x3ef field public static final int PHYSICAL_CELL_ID_UNKNOWN = -1; // 0xffffffff } @@ -41408,6 +41444,49 @@ package android.telephony { field public static final int INVALID = 2147483647; // 0x7fffffff } + public final class SignalStrengthUpdateRequest implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.Collection<android.telephony.SignalThresholdInfo> getSignalThresholdInfos(); + method public boolean isReportingRequestedWhileIdle(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SignalStrengthUpdateRequest> CREATOR; + } + + public static final class SignalStrengthUpdateRequest.Builder { + ctor public SignalStrengthUpdateRequest.Builder(); + method @NonNull public android.telephony.SignalStrengthUpdateRequest build(); + method @NonNull public android.telephony.SignalStrengthUpdateRequest.Builder setReportingRequestedWhileIdle(boolean); + method @NonNull public android.telephony.SignalStrengthUpdateRequest.Builder setSignalThresholdInfos(@NonNull java.util.Collection<android.telephony.SignalThresholdInfo>); + } + + public final class SignalThresholdInfo implements android.os.Parcelable { + method public int describeContents(); + method public static int getMaximumNumberOfThresholdsAllowed(); + method public static int getMinimumNumberOfThresholdsAllowed(); + method public int getRadioAccessNetworkType(); + method public int getSignalMeasurementType(); + method @NonNull public int[] getThresholds(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SignalThresholdInfo> CREATOR; + field public static final int SIGNAL_MEASUREMENT_TYPE_RSCP = 2; // 0x2 + field public static final int SIGNAL_MEASUREMENT_TYPE_RSRP = 3; // 0x3 + field public static final int SIGNAL_MEASUREMENT_TYPE_RSRQ = 4; // 0x4 + field public static final int SIGNAL_MEASUREMENT_TYPE_RSSI = 1; // 0x1 + field public static final int SIGNAL_MEASUREMENT_TYPE_RSSNR = 5; // 0x5 + field public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRP = 6; // 0x6 + field public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRQ = 7; // 0x7 + field public static final int SIGNAL_MEASUREMENT_TYPE_SSSINR = 8; // 0x8 + field public static final int SIGNAL_MEASUREMENT_TYPE_UNKNOWN = 0; // 0x0 + } + + public static final class SignalThresholdInfo.Builder { + ctor public SignalThresholdInfo.Builder(); + method @NonNull public android.telephony.SignalThresholdInfo build(); + method @NonNull public android.telephony.SignalThresholdInfo.Builder setRadioAccessNetworkType(int); + method @NonNull public android.telephony.SignalThresholdInfo.Builder setSignalMeasurementType(int); + method @NonNull public android.telephony.SignalThresholdInfo.Builder setThresholds(@NonNull int[]); + } + public final class SmsManager { method public String createAppSpecificSmsToken(android.app.PendingIntent); method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent); @@ -49319,8 +49398,9 @@ package android.view { method public void show(int); field public static final int APPEARANCE_LIGHT_NAVIGATION_BARS = 16; // 0x10 field public static final int APPEARANCE_LIGHT_STATUS_BARS = 8; // 0x8 - field public static final int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1; // 0x1 - field public static final int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0; // 0x0 + field public static final int BEHAVIOR_DEFAULT = 1; // 0x1 + field @Deprecated public static final int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1; // 0x1 + field @Deprecated public static final int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0; // 0x0 field public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2; // 0x2 } @@ -51683,6 +51763,10 @@ package android.view.textservice { } public final class TextServicesManager { + method @Nullable public android.view.textservice.SpellCheckerInfo getCurrentSpellChecker(); + method @Nullable public android.view.textservice.SpellCheckerSubtype getCurrentSpellCheckerSubtype(boolean); + method @Nullable public java.util.List<android.view.textservice.SpellCheckerInfo> getEnabledSpellCheckersList(); + method public boolean isSpellCheckerEnabled(); method public android.view.textservice.SpellCheckerSession newSpellCheckerSession(android.os.Bundle, java.util.Locale, android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean); } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 2ebcb1984ae0..d91ea2c20473 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -137,6 +137,7 @@ package android { field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER"; field public static final String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS"; field public static final String MANAGE_TIME_AND_ZONE_DETECTION = "android.permission.MANAGE_TIME_AND_ZONE_DETECTION"; + field public static final String MANAGE_UI_TRANSLATION = "android.permission.MANAGE_UI_TRANSLATION"; field public static final String MANAGE_USB = "android.permission.MANAGE_USB"; field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS"; field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE"; @@ -369,6 +370,8 @@ package android.app { method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener); method public void setDeviceLocales(@NonNull android.os.LocaleList); method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public static void setPersistentVrThread(int); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public boolean startProfile(@NonNull android.os.UserHandle); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public boolean stopProfile(@NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull android.os.UserHandle); } @@ -893,6 +896,8 @@ package android.app.admin { field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI"; field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL"; field public static final String EXTRA_PROVISIONING_ORGANIZATION_NAME = "android.app.extra.PROVISIONING_ORGANIZATION_NAME"; + field public static final String EXTRA_PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE = "android.app.extra.PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE"; + field public static final String EXTRA_PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER = "android.app.extra.PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER"; field public static final String EXTRA_PROVISIONING_SUPPORTED_MODES = "android.app.extra.PROVISIONING_SUPPORTED_MODES"; field public static final String EXTRA_PROVISIONING_SUPPORT_URL = "android.app.extra.PROVISIONING_SUPPORT_URL"; field public static final String EXTRA_PROVISIONING_TRIGGER = "android.app.extra.PROVISIONING_TRIGGER"; @@ -1922,7 +1927,6 @@ package android.content { field public static final String BATTERY_STATS_SERVICE = "batterystats"; field public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000 field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000 - field public static final String BUGREPORT_SERVICE = "bugreport"; field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions"; field public static final String CONTEXTHUB_SERVICE = "contexthub"; field public static final String ETHERNET_SERVICE = "ethernet"; @@ -1944,6 +1948,7 @@ package android.content { field public static final String SYSTEM_UPDATE_SERVICE = "system_update"; field public static final String TETHERING_SERVICE = "tethering"; field public static final String TRANSLATION_MANAGER_SERVICE = "transformer"; + field public static final String UI_TRANSLATION_SERVICE = "ui_translation"; field public static final String VR_SERVICE = "vrmanager"; field public static final String WIFI_NL80211_SERVICE = "wifinl80211"; field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager"; @@ -5350,9 +5355,9 @@ package android.media.tv.tuner { method public int disconnectFrontendToCiCam(int); method public int getAvSyncHwId(@NonNull android.media.tv.tuner.filter.Filter); method public long getAvSyncTime(int); + method @Nullable public java.util.List<android.media.tv.tuner.frontend.FrontendInfo> getAvailableFrontendInfos(); method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities(); method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo(); - method @Nullable public java.util.List<android.media.tv.tuner.frontend.FrontendInfo> getFrontendInfoList(); method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]); method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler(); method @Nullable public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener); @@ -7852,24 +7857,10 @@ package android.os { } public final class BugreportManager { - method @RequiresPermission(android.Manifest.permission.DUMP) public void cancelBugreport(); method @RequiresPermission(android.Manifest.permission.DUMP) public void requestBugreport(@NonNull android.os.BugreportParams, @Nullable CharSequence, @Nullable CharSequence); method @RequiresPermission(android.Manifest.permission.DUMP) public void startBugreport(@NonNull android.os.ParcelFileDescriptor, @Nullable android.os.ParcelFileDescriptor, @NonNull android.os.BugreportParams, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback); } - public abstract static class BugreportManager.BugreportCallback { - ctor public BugreportManager.BugreportCallback(); - method public void onEarlyReportFinished(); - method public void onError(int); - method public void onFinished(); - method public void onProgress(@FloatRange(from=0.0f, to=100.0f) float); - field public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5; // 0x5 - field public static final int BUGREPORT_ERROR_INVALID_INPUT = 1; // 0x1 - field public static final int BUGREPORT_ERROR_RUNTIME = 2; // 0x2 - field public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4; // 0x4 - field public static final int BUGREPORT_ERROR_USER_DENIED_CONSENT = 3; // 0x3 - } - public final class BugreportParams { ctor public BugreportParams(int); method public int getMode(); @@ -10242,6 +10233,7 @@ package android.telecom { method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle(); method @Nullable public final String getTelecomCallId(); method @Deprecated public void onAudioStateChanged(android.telecom.AudioState); + method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean); method public final void resetConnectionTime(); method public void setCallDirection(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long); @@ -10418,6 +10410,7 @@ package android.telecom { } public final class RemoteConnection { + method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean); method @Deprecated public void setAudioState(android.telecom.AudioState); } @@ -10601,6 +10594,7 @@ package android.telephony { public static final class CarrierConfigManager.Wifi { field public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT = "wifi.hotspot_maximum_client_count"; field public static final String KEY_PREFIX = "wifi."; + field public static final String KEY_SUGGESTION_SSID_LIST_WITH_MAC_RANDOMIZATION_DISABLED = "wifi.suggestion_ssid_list_with_mac_randomization_disabled"; } public final class CarrierRestrictionRules implements android.os.Parcelable { @@ -13445,6 +13439,17 @@ package android.view.contentcapture { } +package android.view.translation { + + public final class UiTranslationManager { + method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void finishTranslation(int); + method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void pauseTranslation(int); + method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void resumeTranslation(int); + method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void startTranslation(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, @NonNull java.util.List<android.view.autofill.AutofillId>, int); + } + +} + package android.webkit { public abstract class CookieManager { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 015ae64a9257..9e83136b978d 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -254,6 +254,7 @@ package android.app { method public boolean isImportanceLockedByOEM(); method public void lockFields(int); method public void setDeleted(boolean); + method public void setDeletedTimeMs(long); method public void setDemoted(boolean); method public void setFgServiceShown(boolean); method public void setImportanceLockedByCriticalDeviceFunction(boolean); @@ -385,18 +386,35 @@ package android.app.admin { method @NonNull public static String operationToString(int); method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, boolean); field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED"; + field public static final int OPERATION_CLEAR_APPLICATION_USER_DATA = 23; // 0x17 field public static final int OPERATION_CREATE_AND_MANAGE_USER = 5; // 0x5 + field public static final int OPERATION_INSTALL_CA_CERT = 24; // 0x18 + field public static final int OPERATION_INSTALL_KEY_PAIR = 25; // 0x19 + field public static final int OPERATION_INSTALL_SYSTEM_UPDATE = 26; // 0x1a field public static final int OPERATION_LOCK_NOW = 1; // 0x1 field public static final int OPERATION_LOGOUT_USER = 9; // 0x9 field public static final int OPERATION_REBOOT = 7; // 0x7 + field public static final int OPERATION_REMOVE_ACTIVE_ADMIN = 27; // 0x1b + field public static final int OPERATION_REMOVE_KEY_PAIR = 28; // 0x1c field public static final int OPERATION_REMOVE_USER = 6; // 0x6 + field public static final int OPERATION_REQUEST_BUGREPORT = 29; // 0x1d + field public static final int OPERATION_SET_ALWAYS_ON_VPN_PACKAGE = 30; // 0x1e field public static final int OPERATION_SET_APPLICATION_HIDDEN = 15; // 0xf field public static final int OPERATION_SET_APPLICATION_RESTRICTIONS = 16; // 0x10 + field public static final int OPERATION_SET_CAMERA_DISABLED = 31; // 0x1f + field public static final int OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY = 32; // 0x20 + field public static final int OPERATION_SET_GLOBAL_PRIVATE_DNS = 33; // 0x21 field public static final int OPERATION_SET_KEEP_UNINSTALLED_PACKAGES = 17; // 0x11 field public static final int OPERATION_SET_KEYGUARD_DISABLED = 12; // 0xc field public static final int OPERATION_SET_LOCK_TASK_FEATURES = 18; // 0x12 field public static final int OPERATION_SET_LOCK_TASK_PACKAGES = 19; // 0x13 + field public static final int OPERATION_SET_LOGOUT_ENABLED = 34; // 0x22 + field public static final int OPERATION_SET_MASTER_VOLUME_MUTED = 35; // 0x23 + field public static final int OPERATION_SET_OVERRIDE_APNS_ENABLED = 36; // 0x24 field public static final int OPERATION_SET_PACKAGES_SUSPENDED = 20; // 0x14 + field public static final int OPERATION_SET_PERMISSION_GRANT_STATE = 37; // 0x25 + field public static final int OPERATION_SET_PERMISSION_POLICY = 38; // 0x26 + field public static final int OPERATION_SET_RESTRICTIONS_PROVIDER = 39; // 0x27 field public static final int OPERATION_SET_STATUS_BAR_DISABLED = 13; // 0xd field public static final int OPERATION_SET_SYSTEM_SETTING = 11; // 0xb field public static final int OPERATION_SET_SYSTEM_UPDATE_POLICY = 14; // 0xe @@ -406,6 +424,7 @@ package android.app.admin { field public static final int OPERATION_START_USER_IN_BACKGROUND = 3; // 0x3 field public static final int OPERATION_STOP_USER = 4; // 0x4 field public static final int OPERATION_SWITCH_USER = 2; // 0x2 + field public static final int OPERATION_UNINSTALL_CA_CERT = 40; // 0x28 field public static final int OPERATION_WIPE_DATA = 8; // 0x8 } @@ -449,13 +468,10 @@ package android.app.prediction { package android.app.role { - public class RoleControllerManager { - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - } - public final class RoleManager { method @Nullable public String getSmsRoleHolder(int); + method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); + method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); } } @@ -1351,11 +1367,10 @@ package android.os { public static class VibrationEffect.Prebaked extends android.os.VibrationEffect implements android.os.Parcelable { ctor public VibrationEffect.Prebaked(android.os.Parcel); - ctor public VibrationEffect.Prebaked(int, boolean); + ctor public VibrationEffect.Prebaked(int, boolean, int); method public long getDuration(); method public int getEffectStrength(); method public int getId(); - method public void setEffectStrength(int); method public boolean shouldFallback(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect.Prebaked> CREATOR; @@ -1609,6 +1624,11 @@ package android.provider { package android.security { + public final class KeyChain { + method @RequiresPermission("android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP") public static boolean removeCredentialManagementApp(@NonNull android.content.Context); + method @RequiresPermission("android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP") public static boolean setCredentialManagementApp(@NonNull android.content.Context, @NonNull String, @NonNull android.security.AppUriAuthenticationPolicy); + } + public class KeyStoreException extends java.lang.Exception { ctor public KeyStoreException(int, String); method public int getErrorCode(); @@ -2376,10 +2396,6 @@ package android.view.textservice { field public static final int SUBTYPE_ID_NONE = 0; // 0x0 } - public final class TextServicesManager { - method public boolean isSpellCheckerEnabled(); - } - } package android.widget { diff --git a/core/java/android/accessibilityservice/OWNERS b/core/java/android/accessibilityservice/OWNERS index c6f42f719caa..a31cfae995b2 100644 --- a/core/java/android/accessibilityservice/OWNERS +++ b/core/java/android/accessibilityservice/OWNERS @@ -1,4 +1,4 @@ svetoslavganov@google.com pweaver@google.com rhedjao@google.com -qasid@google.com +ryanlwlin@google.com diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 8c62e9c4b2e8..520959cc40e7 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -3883,6 +3883,52 @@ public class ActivityManager { } /** + * Starts a profile. + * To be used with non-managed profiles, managed profiles should use + * {@link UserManager#requestQuietModeEnabled} + * + * @param userHandle user handle of the profile. + * @return true if the profile has been successfully started or if the profile is already + * running, false if profile failed to start. + * @throws IllegalArgumentException if {@code userHandle} is not a profile. + * + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) + public boolean startProfile(@NonNull UserHandle userHandle) { + try { + return getService().startProfile(userHandle.getIdentifier()); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Stops a running profile. + * To be used with non-managed profiles, managed profiles should use + * {@link UserManager#requestQuietModeEnabled} + * + * @param userHandle user handle of the profile. + * @return true if the profile has been successfully stopped or is already stopped. Otherwise + * the exceptions listed below are thrown. + * @throws IllegalArgumentException if {@code userHandle} is not a profile. + * + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) + public boolean stopProfile(@NonNull UserHandle userHandle) { + try { + return getService().stopProfile(userHandle.getIdentifier()); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * Updates the MCC (Mobile Country Code) and MNC (Mobile Network Code) in the * system configuration. * diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index f7f42a6e2713..986051cccd51 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -98,7 +98,7 @@ public abstract class ActivityManagerInternal { public abstract void killForegroundAppsForUser(@UserIdInt int userId); /** - * Sets how long a {@link PendingIntent} can be temporarily whitelist to by bypass restrictions + * Sets how long a {@link PendingIntent} can be temporarily allowlisted to bypass restrictions * such as Power Save mode. * @param target * @param whitelistToken @@ -132,9 +132,14 @@ public abstract class ActivityManagerInternal { /** * Update information about which app IDs are on the temp whitelist. - */ - public abstract void updateDeviceIdleTempWhitelist(int[] appids, int changingAppId, - boolean adding); + * @param appids the updated list of appIds in temp allowlist. + * @param changingUid uid to add or remove to temp allowlist. + * @param adding true to add to temp allowlist, false to remove from temp allowlist. + * @param durationMs when adding is true, the duration to be in temp allowlist. + * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}. + */ + public abstract void updateDeviceIdleTempWhitelist(int[] appids, int changingUid, + boolean adding, long durationMs, @BroadcastOptions.TempAllowListType int type); /** * Get the procstate for the UID. The return value will be between diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index f541e1a7dab0..d0d5df91a2a3 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -54,6 +54,7 @@ import android.view.RemoteAnimationAdapter; import android.view.View; import android.view.ViewGroup; import android.view.Window; +import android.window.IRemoteTransition; import android.window.WindowContainerToken; import java.lang.annotation.Retention; @@ -298,6 +299,8 @@ public class ActivityOptions { private static final String KEY_SPECS_FUTURE = "android:activity.specsFuture"; private static final String KEY_REMOTE_ANIMATION_ADAPTER = "android:activity.remoteAnimationAdapter"; + private static final String KEY_REMOTE_TRANSITION = + "android:activity.remoteTransition"; /** * @see #setLaunchCookie @@ -380,6 +383,7 @@ public class ActivityOptions { private IAppTransitionAnimationSpecsFuture mSpecsFuture; private RemoteAnimationAdapter mRemoteAnimationAdapter; private IBinder mLaunchCookie; + private IRemoteTransition mRemoteTransition; /** * Create an ActivityOptions specifying a custom animation to run when @@ -959,6 +963,21 @@ public class ActivityOptions { return opts; } + /** + * Create an {@link ActivityOptions} instance that lets the application control the entire + * animation using a {@link RemoteAnimationAdapter}. + * @hide + */ + @RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS) + public static ActivityOptions makeRemoteAnimation(RemoteAnimationAdapter remoteAnimationAdapter, + IRemoteTransition remoteTransition) { + final ActivityOptions opts = new ActivityOptions(); + opts.mRemoteAnimationAdapter = remoteAnimationAdapter; + opts.mAnimationType = ANIM_REMOTE_ANIMATION; + opts.mRemoteTransition = remoteTransition; + return opts; + } + /** @hide */ public boolean getLaunchTaskBehind() { return mAnimationType == ANIM_LAUNCH_TASK_BEHIND; @@ -1064,6 +1083,8 @@ public class ActivityOptions { } mRemoteAnimationAdapter = opts.getParcelable(KEY_REMOTE_ANIMATION_ADAPTER); mLaunchCookie = opts.getBinder(KEY_LAUNCH_COOKIE); + mRemoteTransition = IRemoteTransition.Stub.asInterface(opts.getBinder( + KEY_REMOTE_TRANSITION)); } /** @@ -1223,6 +1244,11 @@ public class ActivityOptions { } /** @hide */ + public IRemoteTransition getRemoteTransition() { + return mRemoteTransition; + } + + /** @hide */ public static ActivityOptions fromBundle(Bundle bOptions) { return bOptions != null ? new ActivityOptions(bOptions) : null; } @@ -1724,6 +1750,9 @@ public class ActivityOptions { if (mLaunchCookie != null) { b.putBinder(KEY_LAUNCH_COOKIE, mLaunchCookie); } + if (mRemoteTransition != null) { + b.putBinder(KEY_REMOTE_TRANSITION, mRemoteTransition.asBinder()); + } return b; } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index a23dd35fa55f..161b7313b893 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1645,7 +1645,7 @@ public class AppOpsManager { */ private static int[] sOpToSwitch = new int[] { OP_COARSE_LOCATION, // COARSE_LOCATION - OP_COARSE_LOCATION, // FINE_LOCATION + OP_FINE_LOCATION, // FINE_LOCATION OP_COARSE_LOCATION, // GPS OP_VIBRATE, // VIBRATE OP_READ_CONTACTS, // READ_CONTACTS diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index c1ed7b2ec55a..bac502538740 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1995,8 +1995,7 @@ class ContextImpl extends Context { } private static boolean isUiComponent(String name) { - return WINDOW_SERVICE.equals(name) || LAYOUT_INFLATER_SERVICE.equals(name) - || WALLPAPER_SERVICE.equals(name); + return WINDOW_SERVICE.equals(name) || LAYOUT_INFLATER_SERVICE.equals(name); } @Override diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java index 9fdff5979cd0..f7097fab6b9e 100644 --- a/core/java/android/app/ExitTransitionCoordinator.java +++ b/core/java/android/app/ExitTransitionCoordinator.java @@ -470,9 +470,6 @@ public class ExitTransitionCoordinator extends ActivityTransitionCoordinator { || mSharedElementsHidden)) { finish(); } - if (!mIsReturning && mExitNotified) { - mExitCallbacks = null; // don't need it anymore - } } private void finish() { diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 1b8f04902842..0019fd1908b3 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -686,4 +686,22 @@ interface IActivityManager { * {@link android.content.pm.PackageManager#getHoldLockToken()}. */ void holdLock(in IBinder token, in int durationMs); + + /** + * Starts a profile. + * @param userId the user id of the profile. + * @return true if the profile has been successfully started or if the profile is already + * running, false if profile failed to start. + * @throws IllegalArgumentException if the user is not a profile. + */ + boolean startProfile(int userId); + + /** + * Stops a profile. + * @param userId the user id of the profile. + * @return true if the profile has been successfully stopped or is already stopped. Otherwise + * the exceptions listed below are thrown. + * @throws IllegalArgumentException if the user is not a profile. + */ + boolean stopProfile(int userId); } diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index b1a8f9b0ba33..323af8211d76 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -32,6 +32,7 @@ import android.os.Parcelable; import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.text.TextUtils; +import android.util.Slog; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; @@ -111,6 +112,7 @@ public final class NotificationChannel implements Parcelable { private static final String ATT_CONVERSATION_ID = "conv_id"; private static final String ATT_IMP_CONVERSATION = "imp_conv"; private static final String ATT_DEMOTE = "dem"; + private static final String ATT_DELETED_TIME_MS = "del_time"; private static final String DELIMITER = ","; /** @@ -183,6 +185,7 @@ public final class NotificationChannel implements Parcelable { NotificationManager.IMPORTANCE_UNSPECIFIED; private static final boolean DEFAULT_DELETED = false; private static final boolean DEFAULT_SHOW_BADGE = true; + private static final long DEFAULT_DELETION_TIME_MS = -1; @UnsupportedAppUsage private String mId; @@ -214,6 +217,7 @@ public final class NotificationChannel implements Parcelable { private String mConversationId = null; private boolean mDemoted = false; private boolean mImportantConvo = false; + private long mDeletedTime = DEFAULT_DELETION_TIME_MS; /** * Creates a notification channel. @@ -282,6 +286,7 @@ public final class NotificationChannel implements Parcelable { mConversationId = in.readString(); mDemoted = in.readBoolean(); mImportantConvo = in.readBoolean(); + mDeletedTime = in.readLong(); } @Override @@ -341,6 +346,7 @@ public final class NotificationChannel implements Parcelable { dest.writeString(mConversationId); dest.writeBoolean(mDemoted); dest.writeBoolean(mImportantConvo); + dest.writeLong(mDeletedTime); } /** @@ -378,6 +384,14 @@ public final class NotificationChannel implements Parcelable { * @hide */ @TestApi + public void setDeletedTimeMs(long time) { + mDeletedTime = time; + } + + /** + * @hide + */ + @TestApi public void setImportantConversation(boolean importantConvo) { mImportantConvo = importantConvo; } @@ -766,6 +780,13 @@ public final class NotificationChannel implements Parcelable { /** * @hide */ + public long getDeletedTimeMs() { + return mDeletedTime; + } + + /** + * @hide + */ @SystemApi public int getUserLockedFields() { return mUserLockedFields; @@ -906,6 +927,8 @@ public final class NotificationChannel implements Parcelable { enableVibration(safeBool(parser, ATT_VIBRATION_ENABLED, false)); setShowBadge(safeBool(parser, ATT_SHOW_BADGE, false)); setDeleted(safeBool(parser, ATT_DELETED, false)); + setDeletedTimeMs(XmlUtils.readLongAttribute( + parser, ATT_DELETED_TIME_MS, DEFAULT_DELETION_TIME_MS)); setGroup(parser.getAttributeValue(null, ATT_GROUP)); lockFields(safeInt(parser, ATT_USER_LOCKED, 0)); setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false)); @@ -1024,6 +1047,9 @@ public final class NotificationChannel implements Parcelable { if (isDeleted()) { out.attributeBoolean(null, ATT_DELETED, isDeleted()); } + if (getDeletedTimeMs() >= 0) { + out.attributeLong(null, ATT_DELETED_TIME_MS, getDeletedTimeMs()); + } if (getGroup() != null) { out.attribute(null, ATT_GROUP, getGroup()); } @@ -1091,6 +1117,7 @@ public final class NotificationChannel implements Parcelable { record.put(ATT_VIBRATION, longArrayToString(getVibrationPattern())); record.put(ATT_SHOW_BADGE, Boolean.toString(canShowBadge())); record.put(ATT_DELETED, Boolean.toString(isDeleted())); + record.put(ATT_DELETED_TIME_MS, Long.toString(getDeletedTimeMs())); record.put(ATT_GROUP, getGroup()); record.put(ATT_BLOCKABLE_SYSTEM, isBlockable()); record.put(ATT_ALLOW_BUBBLE, getAllowBubbles()); @@ -1182,6 +1209,7 @@ public final class NotificationChannel implements Parcelable { && mVibrationEnabled == that.mVibrationEnabled && mShowBadge == that.mShowBadge && isDeleted() == that.isDeleted() + && getDeletedTimeMs() == that.getDeletedTimeMs() && isBlockable() == that.isBlockable() && mAllowBubbles == that.mAllowBubbles && Objects.equals(getId(), that.getId()) @@ -1205,8 +1233,8 @@ public final class NotificationChannel implements Parcelable { int result = Objects.hash(getId(), getName(), mDesc, getImportance(), mBypassDnd, getLockscreenVisibility(), getSound(), mLights, getLightColor(), getUserLockedFields(), - isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(), - getAudioAttributes(), isBlockable(), mAllowBubbles, + isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getDeletedTimeMs(), + getGroup(), getAudioAttributes(), isBlockable(), mAllowBubbles, mImportanceLockedByOEM, mImportanceLockedDefaultApp, mOriginalImportance, mParentId, mConversationId, mDemoted, mImportantConvo); result = 31 * result + Arrays.hashCode(mVibration); @@ -1247,6 +1275,7 @@ public final class NotificationChannel implements Parcelable { + ", mVibrationEnabled=" + mVibrationEnabled + ", mShowBadge=" + mShowBadge + ", mDeleted=" + mDeleted + + ", mDeletedTimeMs=" + mDeletedTime + ", mGroup='" + mGroup + '\'' + ", mAudioAttributes=" + mAudioAttributes + ", mBlockableSystem=" + mBlockableSystem diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index dd016a2cf78e..bbda87132559 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -31,7 +31,6 @@ import android.app.contentsuggestions.IContentSuggestionsManager; import android.app.job.JobSchedulerFrameworkInitializer; import android.app.people.PeopleManager; import android.app.prediction.AppPredictionManager; -import android.app.role.RoleControllerManager; import android.app.role.RoleManager; import android.app.search.SearchUiManager; import android.app.slice.SliceManager; @@ -211,6 +210,7 @@ import android.view.textclassifier.TextClassificationManager; import android.view.textservice.TextServicesManager; import android.view.translation.ITranslationManager; import android.view.translation.TranslationManager; +import android.view.translation.UiTranslationManager; import com.android.internal.app.IAppOpsService; import com.android.internal.app.IBatteryStats; @@ -1188,6 +1188,19 @@ public final class SystemServiceRegistry { return null; }}); + registerService(Context.UI_TRANSLATION_SERVICE, UiTranslationManager.class, + new CachedServiceFetcher<UiTranslationManager>() { + @Override + public UiTranslationManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getService(Context.TRANSLATION_MANAGER_SERVICE); + ITranslationManager service = ITranslationManager.Stub.asInterface(b); + if (service != null) { + return new UiTranslationManager(ctx.getOuterContext(), service); + } + return null; + }}); + registerService(Context.SEARCH_UI_SERVICE, SearchUiManager.class, new CachedServiceFetcher<SearchUiManager>() { @Override @@ -1315,14 +1328,6 @@ public final class SystemServiceRegistry { return new RoleManager(ctx.getOuterContext()); }}); - registerService(Context.ROLE_CONTROLLER_SERVICE, RoleControllerManager.class, - new CachedServiceFetcher<RoleControllerManager>() { - @Override - public RoleControllerManager createService(ContextImpl ctx) - throws ServiceNotFoundException { - return new RoleControllerManager(ctx.getOuterContext()); - }}); - registerService(Context.DYNAMIC_SYSTEM_SERVICE, DynamicSystemManager.class, new CachedServiceFetcher<DynamicSystemManager>() { @Override diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index ab0901da632f..ac2f22357013 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -63,6 +63,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.ParcelFileDescriptor; import android.os.RemoteException; +import android.os.StrictMode; import android.os.SystemProperties; import android.text.TextUtils; import android.util.Log; @@ -605,7 +606,9 @@ public class WallpaperManager { * or {@code null} if no system wallpaper exists or if the calling application * is not able to access the wallpaper. */ + @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public Drawable getDrawable() { + assertUiContext("getDrawable"); final ColorManagementProxy cmProxy = getColorManagementProxy(); Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, cmProxy); if (bm != null) { @@ -673,6 +676,7 @@ public class WallpaperManager { */ public Drawable getBuiltInDrawable(int outWidth, int outHeight, boolean scaleToFit, float horizontalAlignment, float verticalAlignment, @SetWallpaperFlags int which) { + assertUiContext("getBuiltInDrawable"); if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); throw new RuntimeException(new DeadSystemException()); @@ -838,6 +842,7 @@ public class WallpaperManager { * null pointer if these is none. */ public Drawable peekDrawable() { + assertUiContext("peekDrawable"); final ColorManagementProxy cmProxy = getColorManagementProxy(); Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM, cmProxy); if (bm != null) { @@ -880,6 +885,7 @@ public class WallpaperManager { */ @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public Drawable peekFastDrawable() { + assertUiContext("peekFastDrawable"); final ColorManagementProxy cmProxy = getColorManagementProxy(); Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM, cmProxy); if (bm != null) { @@ -1046,6 +1052,7 @@ public class WallpaperManager { */ @UnsupportedAppUsage public @Nullable WallpaperColors getWallpaperColors(int which, int userId) { + assertUiContext("getWallpaperColors"); return sGlobals.getWallpaperColors(which, userId, mContext.getDisplayId()); } @@ -1261,6 +1268,7 @@ public class WallpaperManager { @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setResource(@RawRes int resid, @SetWallpaperFlags int which) throws IOException { + assertUiContext("setResource"); if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); throw new RuntimeException(new DeadSystemException()); @@ -1581,6 +1589,7 @@ public class WallpaperManager { * @see #getDesiredMinimumHeight() */ public int getDesiredMinimumWidth() { + assertUiContext("getDesiredMinimumWidth"); if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); throw new RuntimeException(new DeadSystemException()); @@ -1609,6 +1618,7 @@ public class WallpaperManager { * @see #getDesiredMinimumWidth() */ public int getDesiredMinimumHeight() { + assertUiContext("getDesiredMinimumHeight"); if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); throw new RuntimeException(new DeadSystemException()); @@ -1639,6 +1649,7 @@ public class WallpaperManager { * @param minimumHeight Desired minimum height */ public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) { + assertUiContext("suggestDesiredDimensions"); try { /** * The framework makes no attempt to limit the window size @@ -1694,6 +1705,7 @@ public class WallpaperManager { */ @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_HINTS) public void setDisplayPadding(Rect padding) { + assertUiContext("setDisplayPadding"); try { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); @@ -1946,6 +1958,7 @@ public class WallpaperManager { */ @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public void clear() throws IOException { + assertUiContext("clear"); setStream(openDefaultWallpaper(mContext, FLAG_SYSTEM), null, false); } @@ -2094,6 +2107,10 @@ public class WallpaperManager { return mCmProxy; } + private void assertUiContext(final String methodName) { + StrictMode.assertUiContext(mContext, methodName); + } + /** * A hidden class to help {@link Globals#getCurrentWallpaperLocked} handle color management. * @hide diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 71af5e33cc6a..54e1ac43f208 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2536,6 +2536,22 @@ public class DevicePolicyManager { "android.app.extra.PROVISIONING_SUPPORTED_MODES"; /** + * A boolean extra which determines whether to skip the ownership disclaimer screen during the + * provisioning flow. The default value is {@code false}. + * + * If the value is {@code true}, it is the responsibility of the provisioning initiator to + * show the relevant disclaimer. + * + * <p>This extra is only respected when provided alongside the {@link + * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent action. + * + * @hide + */ + @SystemApi + public static final String EXTRA_PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER = + "android.app.extra.PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER"; + + /** * An {@link ArrayList} of {@link Integer} extra specifying the allowed provisioning modes. * <p>This extra will be passed to the admin app's {@link #ACTION_GET_PROVISIONING_MODE} * activity, whose result intent must contain {@link #EXTRA_PROVISIONING_MODE} set to one of @@ -2564,6 +2580,30 @@ public class DevicePolicyManager { public static final int PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE = 3; /** + * A {@code boolean} flag that indicates whether the provisioning flow should return before + * starting the admin app's {@link #ACTION_ADMIN_POLICY_COMPLIANCE} handler. The default value + * is {@code true}. + * + * <p>If this extra is set to {@code true}, then when the provisioning flow returns back to the + * provisioning initiator, provisioning will not be complete. The provisioning initiator can + * use this opportunity to do its own preparatory steps prior to the launch of the admin app's + * {@link #ACTION_ADMIN_POLICY_COMPLIANCE} handler. It is the responsibility of the + * provisioning initiator to ensure that the provisioning flow is then resumed and completed. + * + * <p>If this extra is set to {@code false}, then when the provisioning flow returns back to + * the provisioning initiator, provisioning will be complete. Note that device owner + * provisioning is not currently supported for the this scenario. + * + * <p>This extra is only respected when provided alongside the {@link + * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent action. + * + * @hide + */ + @SystemApi + public static final String EXTRA_PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE = + "android.app.extra.PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE"; + + /** * Activity action: Starts the administrator to show policy compliance for the provisioning. * This action is used any time that the administrator has an opportunity to show policy * compliance before the end of setup wizard. This could happen as part of the admin-integrated @@ -2690,6 +2730,60 @@ public class DevicePolicyManager { /** @hide */ @TestApi public static final int OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES = 22; + /** @hide */ + @TestApi + public static final int OPERATION_CLEAR_APPLICATION_USER_DATA = 23; + /** @hide */ + @TestApi + public static final int OPERATION_INSTALL_CA_CERT = 24; + /** @hide */ + @TestApi + public static final int OPERATION_INSTALL_KEY_PAIR = 25; + /** @hide */ + @TestApi + public static final int OPERATION_INSTALL_SYSTEM_UPDATE = 26; + /** @hide */ + @TestApi + public static final int OPERATION_REMOVE_ACTIVE_ADMIN = 27; + /** @hide */ + @TestApi + public static final int OPERATION_REMOVE_KEY_PAIR = 28; + /** @hide */ + @TestApi + public static final int OPERATION_REQUEST_BUGREPORT = 29; + /** @hide */ + @TestApi + public static final int OPERATION_SET_ALWAYS_ON_VPN_PACKAGE = 30; + /** @hide */ + @TestApi + public static final int OPERATION_SET_CAMERA_DISABLED = 31; + /** @hide */ + @TestApi + public static final int OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY = 32; + /** @hide */ + @TestApi + public static final int OPERATION_SET_GLOBAL_PRIVATE_DNS = 33; + /** @hide */ + @TestApi + public static final int OPERATION_SET_LOGOUT_ENABLED = 34; + /** @hide */ + @TestApi + public static final int OPERATION_SET_MASTER_VOLUME_MUTED = 35; + /** @hide */ + @TestApi + public static final int OPERATION_SET_OVERRIDE_APNS_ENABLED = 36; + /** @hide */ + @TestApi + public static final int OPERATION_SET_PERMISSION_GRANT_STATE = 37; + /** @hide */ + @TestApi + public static final int OPERATION_SET_PERMISSION_POLICY = 38; + /** @hide */ + @TestApi + public static final int OPERATION_SET_RESTRICTIONS_PROVIDER = 39; + /** @hide */ + @TestApi + public static final int OPERATION_UNINSTALL_CA_CERT = 40; private static final String PREFIX_OPERATION = "OPERATION_"; @@ -2716,7 +2810,25 @@ public class DevicePolicyManager { OPERATION_SET_LOCK_TASK_PACKAGES, OPERATION_SET_PACKAGES_SUSPENDED, OPERATION_SET_TRUST_AGENT_CONFIGURATION, - OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES + OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES, + OPERATION_CLEAR_APPLICATION_USER_DATA, + OPERATION_INSTALL_CA_CERT, + OPERATION_INSTALL_KEY_PAIR, + OPERATION_INSTALL_SYSTEM_UPDATE, + OPERATION_REMOVE_ACTIVE_ADMIN, + OPERATION_REMOVE_KEY_PAIR, + OPERATION_REQUEST_BUGREPORT, + OPERATION_SET_ALWAYS_ON_VPN_PACKAGE, + OPERATION_SET_CAMERA_DISABLED, + OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY, + OPERATION_SET_GLOBAL_PRIVATE_DNS, + OPERATION_SET_LOGOUT_ENABLED, + OPERATION_SET_MASTER_VOLUME_MUTED, + OPERATION_SET_OVERRIDE_APNS_ENABLED, + OPERATION_SET_PERMISSION_GRANT_STATE, + OPERATION_SET_PERMISSION_POLICY, + OPERATION_SET_RESTRICTIONS_PROVIDER, + OPERATION_UNINSTALL_CA_CERT }) @Retention(RetentionPolicy.SOURCE) public static @interface DevicePolicyOperation { @@ -9823,7 +9935,7 @@ public class DevicePolicyManager { * Designates a specific service component as the provider for making permission requests of a * local or remote administrator of the user. * <p/> - * Only a profile owner can designate the restrictions provider. + * Only a device owner or profile owner can designate the restrictions provider. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param provider The component name of the service that implements diff --git a/core/java/android/app/role/RoleControllerManager.java b/core/java/android/app/role/RoleControllerManager.java index 8dde2c55d7d3..ba1f61290631 100644 --- a/core/java/android/app/role/RoleControllerManager.java +++ b/core/java/android/app/role/RoleControllerManager.java @@ -20,8 +20,6 @@ import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.RequiresPermission; -import android.annotation.SystemService; -import android.annotation.TestApi; import android.app.ActivityThread; import android.content.ComponentName; import android.content.Context; @@ -48,8 +46,6 @@ import java.util.function.Consumer; * * @hide */ -@SystemService(Context.ROLE_CONTROLLER_SERVICE) -@TestApi public class RoleControllerManager { private static final String LOG_TAG = RoleControllerManager.class.getSimpleName(); @@ -199,32 +195,11 @@ public class RoleControllerManager { } /** - * @see RoleControllerService#onIsApplicationQualifiedForRole(String, String) - * - * @deprecated Use {@link #isApplicationVisibleForRole(String, String, Executor, Consumer)} - * instead. - * - * @hide - */ - @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - public void isApplicationQualifiedForRole(@NonNull String roleName, @NonNull String packageName, - @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { - AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { - AndroidFuture<Bundle> future = new AndroidFuture<>(); - service.isApplicationQualifiedForRole(roleName, packageName, - new RemoteCallback(future::complete)); - return future; - }); - propagateCallback(operation, "isApplicationQualifiedForRole", executor, callback); - } - - /** * @see RoleControllerService#onIsApplicationVisibleForRole(String, String) * * @hide */ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - @TestApi public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { @@ -242,7 +217,6 @@ public class RoleControllerManager { * @hide */ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - @TestApi public void isRoleVisible(@NonNull String roleName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java index 8b2e07b09701..0fcf44d2dda6 100644 --- a/core/java/android/app/role/RoleManager.java +++ b/core/java/android/app/role/RoleManager.java @@ -174,6 +174,9 @@ public final class RoleManager { @NonNull private final Object mListenersLock = new Object(); + @NonNull + private final RoleControllerManager mRoleControllerManager; + /** * @hide */ @@ -181,6 +184,7 @@ public final class RoleManager { mContext = context; mService = IRoleManager.Stub.asInterface(ServiceManager.getServiceOrThrow( Context.ROLE_SERVICE)); + mRoleControllerManager = new RoleControllerManager(context); } /** @@ -676,6 +680,44 @@ public final class RoleManager { } } + /** + * Check whether a role should be visible to user. + * + * @param roleName name of the role to check for + * @param executor the executor to execute callback on + * @param callback the callback to receive whether the role should be visible to user + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) + @TestApi + public void isRoleVisible(@NonNull String roleName, + @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { + mRoleControllerManager.isRoleVisible(roleName, executor, callback); + } + + /** + * Check whether an application is visible for a role. + * + * While an application can be qualified for a role, it can still stay hidden from user (thus + * not visible). If an application is visible for a role, we may show things related to the role + * for it, e.g. showing an entry pointing to the role settings in its application info page. + * + * @param roleName the name of the role to check for + * @param packageName the package name of the application to check for + * @param executor the executor to execute callback on + * @param callback the callback to receive whether the application is visible for the role + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) + @TestApi + public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName, + @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { + mRoleControllerManager.isApplicationVisibleForRole(roleName, packageName, executor, + callback); + } + private static class OnRoleHoldersChangedListenerDelegate extends IOnRoleHoldersChangedListener.Stub { diff --git a/core/java/android/app/search/OWNERS b/core/java/android/app/search/OWNERS new file mode 100644 index 000000000000..92835c2b0626 --- /dev/null +++ b/core/java/android/app/search/OWNERS @@ -0,0 +1,2 @@ +hyunyoungs@google.com +sfufa@google.com diff --git a/core/java/android/app/search/SearchAction.java b/core/java/android/app/search/SearchAction.java index 158f9f384603..a76154af63b6 100644 --- a/core/java/android/app/search/SearchAction.java +++ b/core/java/android/app/search/SearchAction.java @@ -70,20 +70,20 @@ public final class SearchAction implements Parcelable { SearchAction(Parcel in) { mId = in.readString(); - mIcon = Icon.CREATOR.createFromParcel(in); mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mIcon = in.readTypedObject(Icon.CREATOR); mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - mPendingIntent = PendingIntent.CREATOR.createFromParcel(in); - mIntent = Intent.CREATOR.createFromParcel(in); + mPendingIntent = in.readTypedObject(PendingIntent.CREATOR); + mIntent = in.readTypedObject(Intent.CREATOR); mUserHandle = in.readTypedObject(UserHandle.CREATOR); - mExtras = in.readBundle(); + mExtras = in.readTypedObject(Bundle.CREATOR); } private SearchAction( @NonNull String id, - @Nullable Icon icon, @NonNull CharSequence title, + @Nullable Icon icon, @Nullable CharSequence subtitle, @Nullable CharSequence contentDescription, @Nullable PendingIntent pendingIntent, @@ -91,8 +91,8 @@ public final class SearchAction implements Parcelable { @Nullable UserHandle userHandle, @Nullable Bundle extras) { mId = Objects.requireNonNull(id); - mIcon = icon; mTitle = Objects.requireNonNull(title); + mIcon = icon; mSubtitle = subtitle; mContentDescription = contentDescription; mPendingIntent = pendingIntent; @@ -192,14 +192,14 @@ public final class SearchAction implements Parcelable { @Override public void writeToParcel(@NonNull Parcel out, int flags) { out.writeString(mId); - out.writeTypedObject(mIcon, flags); TextUtils.writeToParcel(mTitle, out, flags); + out.writeTypedObject(mIcon, flags); TextUtils.writeToParcel(mSubtitle, out, flags); TextUtils.writeToParcel(mContentDescription, out, flags); out.writeTypedObject(mPendingIntent, flags); out.writeTypedObject(mIntent, flags); out.writeTypedObject(mUserHandle, flags); - out.writeBundle(mExtras); + out.writeTypedObject(mExtras, flags); } @Override @@ -235,13 +235,13 @@ public final class SearchAction implements Parcelable { @NonNull private String mId; - @Nullable - private Icon mIcon; - @NonNull private CharSequence mTitle; @Nullable + private Icon mIcon; + + @Nullable private CharSequence mSubtitle; @Nullable @@ -337,7 +337,7 @@ public final class SearchAction implements Parcelable { */ @NonNull public SearchAction build() { - return new SearchAction(mId, mIcon, mTitle, mSubtitle, mContentDescription, + return new SearchAction(mId, mTitle, mIcon, mSubtitle, mContentDescription, mPendingIntent, mIntent, mUserHandle, mExtras); } } diff --git a/core/java/android/app/usage/StorageStats.java b/core/java/android/app/usage/StorageStats.java index 4757557d0fd5..8d25d7bfb11b 100644 --- a/core/java/android/app/usage/StorageStats.java +++ b/core/java/android/app/usage/StorageStats.java @@ -32,6 +32,7 @@ public final class StorageStats implements Parcelable { /** {@hide} */ public long codeBytes; /** {@hide} */ public long dataBytes; /** {@hide} */ public long cacheBytes; + /** {@hide} */ public long externalCacheBytes; /** * Return the size of app. This includes {@code APK} files, optimized @@ -77,6 +78,17 @@ public final class StorageStats implements Parcelable { return cacheBytes; } + /** + * Return the size of all cached data in the primary external/shared storage. + * This includes files stored under + * {@link Context#getExternalCacheDir()}. + * <p> + * Cached data is isolated for each user on a multiuser device. + */ + public @BytesLong long getExternalCacheBytes() { + return externalCacheBytes; + } + /** {@hide} */ public StorageStats() { } @@ -86,6 +98,7 @@ public final class StorageStats implements Parcelable { this.codeBytes = in.readLong(); this.dataBytes = in.readLong(); this.cacheBytes = in.readLong(); + this.externalCacheBytes = in.readLong(); } @Override @@ -98,6 +111,7 @@ public final class StorageStats implements Parcelable { dest.writeLong(codeBytes); dest.writeLong(dataBytes); dest.writeLong(cacheBytes); + dest.writeLong(externalCacheBytes); } public static final @android.annotation.NonNull Creator<StorageStats> CREATOR = new Creator<StorageStats>() { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 5ccceca9a69d..219014076c31 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4520,6 +4520,15 @@ public abstract class Context { public static final String TRANSLATION_MANAGER_SERVICE = "transformer"; /** + * Official published name of the translation service which supports ui translation function. + * + * @hide + * @see #getSystemService(String) + */ + @SystemApi + public static final String UI_TRANSLATION_SERVICE = "ui_translation"; + + /** * Used for getting content selections and classifications for task snapshots. * * @hide @@ -4833,16 +4842,6 @@ public abstract class Context { public static final String ROLE_SERVICE = "role"; /** - * Official published name of the (internal) role controller service. - * - * @see #getSystemService(String) - * @see android.app.role.RoleControllerService - * - * @hide - */ - public static final String ROLE_CONTROLLER_SERVICE = "role_controller"; - - /** * Use with {@link #getSystemService(String)} to retrieve a * {@link android.hardware.camera2.CameraManager} for interacting with * camera devices. @@ -5096,9 +5095,7 @@ public abstract class Context { * Service to capture a bugreport. * @see #getSystemService(String) * @see android.os.BugreportManager - * @hide */ - @SystemApi public static final String BUGREPORT_SERVICE = "bugreport"; /** diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 13a138102800..7843d97aa411 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3660,7 +3660,7 @@ public class Intent implements Parcelable, Cloneable { /** * Broadcast sent by the system when a user is started. Carries an extra - * EXTRA_USER_HANDLE that has the userHandle of the user. This is only sent to + * {@link EXTRA_USER_HANDLE} that has the userHandle of the user. This is only sent to * registered receivers, not manifest receivers. It is sent to the user * that has been started. This is sent as a foreground * broadcast, since it is part of a visible user interaction; be as quick @@ -3672,7 +3672,7 @@ public class Intent implements Parcelable, Cloneable { /** * Broadcast sent when a user is in the process of starting. Carries an extra - * EXTRA_USER_HANDLE that has the userHandle of the user. This is only + * {@link EXTRA_USER_HANDLE} that has the userHandle of the user. This is only * sent to registered receivers, not manifest receivers. It is sent to all * users (including the one that is being started). You must hold * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} to receive @@ -3689,7 +3689,7 @@ public class Intent implements Parcelable, Cloneable { /** * Broadcast sent when a user is going to be stopped. Carries an extra - * EXTRA_USER_HANDLE that has the userHandle of the user. This is only + * {@link EXTRA_USER_HANDLE} that has the userHandle of the user. This is only * sent to registered receivers, not manifest receivers. It is sent to all * users (including the one that is being stopped). You must hold * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} to receive @@ -3707,7 +3707,7 @@ public class Intent implements Parcelable, Cloneable { /** * Broadcast sent to the system when a user is stopped. Carries an extra - * EXTRA_USER_HANDLE that has the userHandle of the user. This is similar to + * {@link EXTRA_USER_HANDLE} that has the userHandle of the user. This is similar to * {@link #ACTION_PACKAGE_RESTARTED}, but for an entire user instead of a * specific package. This is only sent to registered receivers, not manifest * receivers. It is sent to all running users <em>except</em> the one that @@ -3811,6 +3811,22 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.MANAGED_PROFILE_UNAVAILABLE"; /** + * Broadcast sent to the parent user when an associated profile has been started and unlocked. + * Carries an extra {@link #EXTRA_USER} that specifies the {@link UserHandle} of the profile. + * This is only sent to registered receivers, not manifest receivers. + */ + public static final String ACTION_PROFILE_ACCESSIBLE = + "android.intent.action.PROFILE_ACCESSIBLE"; + + /** + * Broadcast sent to the parent user when an associated profile has stopped. + * Carries an extra {@link #EXTRA_USER} that specifies the {@link UserHandle} of the profile. + * This is only sent to registered receivers, not manifest receivers. + */ + public static final String ACTION_PROFILE_INACCESSIBLE = + "android.intent.action.PROFILE_INACCESSIBLE"; + + /** * Broadcast sent to the system user when the 'device locked' state changes for any user. * Carries an extra {@link #EXTRA_USER_HANDLE} that specifies the ID of the user for which * the device was locked or unlocked. diff --git a/core/java/android/content/pm/AppSearchPerson.java b/core/java/android/content/pm/AppSearchPerson.java new file mode 100644 index 000000000000..045c55f28bf1 --- /dev/null +++ b/core/java/android/content/pm/AppSearchPerson.java @@ -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 android.content.pm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.Person; +import android.app.appsearch.AppSearchSchema; +import android.app.appsearch.GenericDocument; +import android.net.UriCodec; + +import com.android.internal.annotations.VisibleForTesting; + +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.UUID; + +/** + * @hide + */ +public class AppSearchPerson extends GenericDocument { + + /** The name of the schema type for {@link Person} documents.*/ + public static final String SCHEMA_TYPE = "Person"; + + public static final String KEY_NAME = "name"; + public static final String KEY_KEY = "key"; + public static final String KEY_IS_BOT = "isBot"; + public static final String KEY_IS_IMPORTANT = "isImportant"; + + private AppSearchPerson(@NonNull GenericDocument document) { + super(document); + } + + public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE) + .addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_NAME) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_KEY) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_IS_BOT) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_IS_IMPORTANT) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).build(); + + /** hide */ + @NonNull + public static AppSearchPerson instance(@NonNull final Person person) { + Objects.requireNonNull(person); + final String id; + if (person.getUri() != null) { + id = person.getUri(); + } else { + // NOTE: an identifier is required even when uri is null. + id = UUID.randomUUID().toString(); + } + return new Builder(id).setName(person.getName()) + .setKey(person.getKey()).setIsBot(person.isBot()) + .setIsImportant(person.isImportant()).build(); + } + + /** hide */ + @NonNull + public Person toPerson() { + String uri; + try { + uri = UriCodec.decode( + getUri(), false /* convertPlus */, StandardCharsets.UTF_8, + true /* throwOnFailure */); + } catch (IllegalArgumentException e) { + uri = null; + } + return new Person.Builder().setName(getPropertyString(KEY_NAME)) + .setUri(uri).setKey(getPropertyString(KEY_KEY)) + .setBot(getPropertyBoolean(KEY_IS_BOT)) + .setImportant(getPropertyBoolean(KEY_IS_IMPORTANT)).build(); + } + + /** @hide */ + @VisibleForTesting + public static class Builder extends GenericDocument.Builder<Builder> { + + public Builder(@NonNull final String id) { + super(id, SCHEMA_TYPE); + } + + /** @hide */ + @NonNull + public Builder setName(@Nullable final CharSequence name) { + if (name != null) { + setPropertyString(KEY_NAME, name.toString()); + } + return this; + } + + /** @hide */ + @NonNull + public Builder setKey(@Nullable final String key) { + if (key != null) { + setPropertyString(KEY_KEY, key); + } + return this; + } + + /** @hide */ + @NonNull + public Builder setIsBot(final boolean isBot) { + setPropertyBoolean(KEY_IS_BOT, isBot); + return this; + } + + /** @hide */ + @NonNull + public Builder setIsImportant(final boolean isImportant) { + setPropertyBoolean(KEY_IS_IMPORTANT, isImportant); + return this; + } + + @NonNull + @Override + public AppSearchPerson build() { + return new AppSearchPerson(super.build()); + } + } +} diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java new file mode 100644 index 000000000000..14b8df86025c --- /dev/null +++ b/core/java/android/content/pm/AppSearchShortcutInfo.java @@ -0,0 +1,616 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.Person; +import android.app.appsearch.AppSearchSchema; +import android.app.appsearch.GenericDocument; +import android.content.ComponentName; +import android.content.Intent; +import android.content.LocusId; +import android.graphics.drawable.Icon; +import android.os.Bundle; +import android.os.PersistableBundle; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.ArraySet; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +/** + * @hide + */ +public class AppSearchShortcutInfo extends GenericDocument { + + /** The name of the schema type for {@link ShortcutInfo} documents.*/ + public static final String SCHEMA_TYPE = "Shortcut"; + + public static final String KEY_PACKAGE_NAME = "packageName"; + public static final String KEY_ACTIVITY = "activity"; + public static final String KEY_TITLE = "title"; + public static final String KEY_TEXT = "text"; + public static final String KEY_DISABLED_MESSAGE = "disabledMessage"; + public static final String KEY_CATEGORIES = "categories"; + public static final String KEY_INTENTS = "intents"; + public static final String KEY_INTENT_PERSISTABLE_EXTRAS = "intentPersistableExtras"; + public static final String KEY_PERSON = "person"; + public static final String KEY_LOCUS_ID = "locusId"; + public static final String KEY_RANK = "rank"; + public static final String KEY_EXTRAS = "extras"; + public static final String KEY_FLAGS = "flags"; + public static final String KEY_ICON_RES_ID = "iconResId"; + public static final String KEY_ICON_RES_NAME = "iconResName"; + public static final String KEY_ICON_URI = "iconUri"; + public static final String KEY_BITMAP_PATH = "bitmapPath"; + public static final String KEY_DISABLED_REASON = "disabledReason"; + + public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE) + .addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_PACKAGE_NAME) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ACTIVITY) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_TITLE) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_TEXT) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_DISABLED_MESSAGE) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_CATEGORIES) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_INTENTS) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_INTENT_PERSISTABLE_EXTRAS) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_PERSON) + .setSchemaType(AppSearchPerson.SCHEMA_TYPE) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_LOCUS_ID) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_RANK) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_EXTRAS) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_FLAGS) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ICON_RES_ID) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ICON_RES_NAME) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ICON_URI) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_BITMAP_PATH) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_DISABLED_REASON) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .build() + + ).build(); + + public AppSearchShortcutInfo(@NonNull GenericDocument document) { + super(document); + } + + /** + * @hide + */ + @NonNull + public static AppSearchShortcutInfo instance(@NonNull final ShortcutInfo shortcutInfo) { + Objects.requireNonNull(shortcutInfo); + return new Builder(shortcutInfo.getId()) + .setActivity(shortcutInfo.getActivity()) + .setPackageName(shortcutInfo.getPackage()) + .setTitle(shortcutInfo.getShortLabel()) + .setText(shortcutInfo.getLongLabel()) + .setDisabledMessage(shortcutInfo.getDisabledMessage()) + .setCategories(shortcutInfo.getCategories()) + .setIntents(shortcutInfo.getIntents()) + .setRank(shortcutInfo.getRank()) + .setExtras(shortcutInfo.getExtras()) + .setCreationTimestampMillis(shortcutInfo.getLastChangedTimestamp()) + .setFlags(shortcutInfo.getFlags()) + .setIconResId(shortcutInfo.getIconResourceId()) + .setIconResName(shortcutInfo.getIconResName()) + .setBitmapPath(shortcutInfo.getBitmapPath()) + .setIconUri(shortcutInfo.getIconUri()) + .setDisabledReason(shortcutInfo.getDisabledReason()) + .setPersons(shortcutInfo.getPersons()) + .setLocusId(shortcutInfo.getLocusId()) + .build(); + } + + /** + * @hide + */ + @NonNull + public ShortcutInfo toShortcutInfo() { + return toShortcutInfo(UserHandle.myUserId()); + } + + /** + * @hide + * TODO: This should be @SystemApi when AppSearchShortcutInfo unhides. + */ + @NonNull + public ShortcutInfo toShortcutInfo(@UserIdInt final int userId) { + final String packageName = getPropertyString(KEY_PACKAGE_NAME); + final String activityString = getPropertyString(KEY_ACTIVITY); + final ComponentName activity = activityString == null + ? null : ComponentName.unflattenFromString(activityString); + // TODO: proper icon handling + // NOTE: bitmap based icons are currently saved in side-channel (see ShortcutBitmapSaver), + // re-creating Icon object at creation time implies turning this function into async since + // loading bitmap is I/O bound. Since ShortcutInfo#getIcon is already annotated with + // @hide and @UnsupportedAppUsage, we could migrate existing usage in platform with + // LauncherApps#getShortcutIconDrawable instead. + final Icon icon = null; + final String title = getPropertyString(KEY_TITLE); + final String text = getPropertyString(KEY_TEXT); + final String disabledMessage = getPropertyString(KEY_DISABLED_MESSAGE); + final String[] categories = getPropertyStringArray(KEY_CATEGORIES); + final Set<String> categoriesSet = categories == null + ? new ArraySet<>() : new ArraySet<>(Arrays.asList(categories)); + final String[] intentsStrings = getPropertyStringArray(KEY_INTENTS); + final Intent[] intents = intentsStrings == null + ? null : Arrays.stream(intentsStrings).map(uri -> { + try { + return Intent.parseUri(uri, /* flags =*/ 0); + } catch (URISyntaxException e) { + // ignore malformed entry + } + return null; + }).toArray(Intent[]::new); + final byte[][] intentExtrasesBytes = getPropertyBytesArray(KEY_INTENT_PERSISTABLE_EXTRAS); + final Bundle[] intentExtrases = intentExtrasesBytes == null + ? null : Arrays.stream(intentExtrasesBytes) + .map(this::transformToBundle).toArray(Bundle[]::new); + if (intents != null) { + for (int i = 0; i < intents.length; i++) { + final Intent intent = intents[i]; + if (intent != null) { + intent.replaceExtras(intentExtrases[i].size() == 0 ? null : intentExtrases[i]); + } + } + } + final Person[] persons = parsePerson(getPropertyDocumentArray(KEY_PERSON)); + final String locusIdString = getPropertyString(KEY_LOCUS_ID); + final LocusId locusId = locusIdString == null ? null : new LocusId(locusIdString); + final int rank = (int) getPropertyLong(KEY_RANK); + final byte[] extrasByte = getPropertyBytes(KEY_EXTRAS); + final PersistableBundle extras = transformToPersistableBundle(extrasByte); + final int flags = parseFlags(getPropertyLongArray(KEY_FLAGS)); + final int iconResId = (int) getPropertyLong(KEY_ICON_RES_ID); + final String iconResName = getPropertyString(KEY_ICON_RES_NAME); + final String iconUri = getPropertyString(KEY_ICON_URI); + final String bitmapPath = getPropertyString(KEY_BITMAP_PATH); + final int disabledReason = (int) getPropertyLong(KEY_DISABLED_REASON); + return new ShortcutInfo( + userId, getUri(), packageName, activity, icon, title, 0, null, + text, 0, null, disabledMessage, 0, null, + categoriesSet, intents, rank, extras, + getCreationTimestampMillis(), flags, iconResId, iconResName, bitmapPath, iconUri, + disabledReason, persons, locusId); + } + + /** @hide */ + @VisibleForTesting + public static class Builder extends GenericDocument.Builder<Builder> { + + public Builder(String id) { + super(id, SCHEMA_TYPE); + } + + /** + * @hide + */ + @NonNull + public Builder setLocusId(@Nullable final LocusId locusId) { + if (locusId != null) { + setPropertyString(KEY_LOCUS_ID, locusId.getId()); + } + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setActivity(@Nullable final ComponentName activity) { + if (activity != null) { + setPropertyString(KEY_ACTIVITY, activity.flattenToShortString()); + } + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setTitle(@Nullable final CharSequence shortLabel) { + if (!TextUtils.isEmpty(shortLabel)) { + setPropertyString(KEY_TITLE, Preconditions.checkStringNotEmpty( + shortLabel, "shortLabel cannot be empty").toString()); + } + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setText(@Nullable final CharSequence longLabel) { + if (!TextUtils.isEmpty(longLabel)) { + setPropertyString(KEY_TEXT, Preconditions.checkStringNotEmpty( + longLabel, "longLabel cannot be empty").toString()); + } + return this; + + } + + /** + * @hide + */ + @NonNull + public Builder setDisabledMessage(@Nullable final CharSequence disabledMessage) { + if (!TextUtils.isEmpty(disabledMessage)) { + setPropertyString(KEY_DISABLED_MESSAGE, Preconditions.checkStringNotEmpty( + disabledMessage, "disabledMessage cannot be empty").toString()); + } + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setCategories(@Nullable final Set<String> categories) { + if (categories != null && !categories.isEmpty()) { + setPropertyString(KEY_CATEGORIES, categories.stream().toArray(String[]::new)); + } + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setIntent(@Nullable final Intent intent) { + if (intent == null) { + return this; + } + return setIntents(new Intent[]{intent}); + } + + /** + * @hide + */ + @NonNull + public Builder setIntents(@Nullable final Intent[] intents) { + if (intents == null || intents.length == 0) { + return this; + } + for (Intent intent : intents) { + Objects.requireNonNull(intent, "intents cannot contain null"); + Objects.requireNonNull(intent.getAction(), "intent's action must be set"); + } + final byte[][] intentExtrases = new byte[intents.length][]; + for (int i = 0; i < intents.length; i++) { + final Intent intent = intents[i]; + final Bundle extras = intent.getExtras(); + intentExtrases[i] = extras == null + ? new byte[0] : transformToByteArray(new PersistableBundle(extras)); + } + + setPropertyString(KEY_INTENTS, Arrays.stream(intents).map(it -> + it.toUri(0)).toArray(String[]::new)); + setPropertyBytes(KEY_INTENT_PERSISTABLE_EXTRAS, intentExtrases); + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setPerson(@Nullable final Person person) { + if (person == null) { + return this; + } + return setPersons(new Person[]{person}); + } + + /** + * @hide + */ + @NonNull + public Builder setPersons(@Nullable final Person[] persons) { + if (persons == null || persons.length == 0) { + return this; + } + setPropertyDocument(KEY_PERSON, + Arrays.stream(persons).map(person -> AppSearchPerson.instance( + Objects.requireNonNull(person, "persons cannot contain null")) + ).toArray(AppSearchPerson[]::new)); + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setRank(final int rank) { + Preconditions.checkArgument((0 <= rank), + "Rank cannot be negative or bigger than MAX_RANK"); + setPropertyLong(KEY_RANK, rank); + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setExtras(@Nullable final PersistableBundle extras) { + if (extras != null) { + setPropertyBytes(KEY_EXTRAS, transformToByteArray(extras)); + } + return this; + } + + /** + * @hide + */ + public Builder setPackageName(@Nullable final String packageName) { + if (!TextUtils.isEmpty(packageName)) { + setPropertyString(KEY_PACKAGE_NAME, packageName); + } + return this; + } + + /** + * @hide + */ + public Builder setFlags(@ShortcutInfo.ShortcutFlags final int flags) { + setPropertyLong(KEY_FLAGS, flattenFlags(flags)); + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setIconResId(@Nullable final int iconResId) { + setPropertyLong(KEY_ICON_RES_ID, iconResId); + return this; + } + + /** + * @hide + */ + public Builder setIconResName(@Nullable final String iconResName) { + if (!TextUtils.isEmpty(iconResName)) { + setPropertyString(KEY_ICON_RES_NAME, iconResName); + } + return this; + } + + /** + * @hide + */ + public Builder setBitmapPath(@Nullable final String bitmapPath) { + if (!TextUtils.isEmpty(bitmapPath)) { + setPropertyString(KEY_BITMAP_PATH, bitmapPath); + } + return this; + } + + /** + * @hide + */ + public Builder setIconUri(@Nullable final String iconUri) { + if (!TextUtils.isEmpty(iconUri)) { + setPropertyString(KEY_ICON_URI, iconUri); + } + return this; + } + + /** + * @hide + */ + public Builder setDisabledReason(@ShortcutInfo.DisabledReason final int disabledReason) { + setPropertyLong(KEY_DISABLED_REASON, disabledReason); + return this; + } + + /** + * @hide + */ + @NonNull + @Override + public AppSearchShortcutInfo build() { + return new AppSearchShortcutInfo(super.build()); + } + } + + /** + * Convert PersistableBundle into byte[] for persistence. + */ + @Nullable + private static byte[] transformToByteArray(@NonNull final PersistableBundle extras) { + Objects.requireNonNull(extras); + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + new PersistableBundle(extras).writeToStream(baos); + return baos.toByteArray(); + } catch (IOException e) { + return null; + } + } + + /** + * Convert byte[] into Bundle. + */ + @Nullable + private Bundle transformToBundle(@Nullable final byte[] extras) { + if (extras == null) { + return null; + } + Objects.requireNonNull(extras); + try (ByteArrayInputStream bais = new ByteArrayInputStream(extras)) { + final Bundle ret = new Bundle(); + ret.putAll(PersistableBundle.readFromStream(bais)); + return ret; + } catch (IOException e) { + return null; + } + } + + /** + * Convert byte[] into PersistableBundle. + */ + @Nullable + private PersistableBundle transformToPersistableBundle(@Nullable final byte[] extras) { + if (extras == null) { + return null; + } + try (ByteArrayInputStream bais = new ByteArrayInputStream(extras)) { + return PersistableBundle.readFromStream(bais); + } catch (IOException e) { + return null; + } + } + + private static long[] flattenFlags(@ShortcutInfo.ShortcutFlags final int flags) { + final List<Integer> flattenedFlags = new ArrayList<>(); + flattenedFlags.add(0); + for (int i = 0; i < 31; i++) { + final int mask = 1 << i; + if ((flags & mask) != 0) { + flattenedFlags.add(mask); + } + } + return flattenedFlags.stream().mapToLong(i -> i).toArray(); + } + + private static int parseFlags(final long[] flags) { + return (int) Arrays.stream(flags).reduce((p, v) -> p | v).getAsLong(); + } + + @NonNull + private static Person[] parsePerson(@Nullable final GenericDocument[] persons) { + return persons == null ? new Person[0] : Arrays.stream(persons).map(it -> + ((AppSearchPerson) it).toPerson()).toArray(Person[]::new); + } +} diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl index 9a73be9d44a4..f72288c670d9 100644 --- a/core/java/android/content/pm/IPackageInstallerSession.aidl +++ b/core/java/android/content/pm/IPackageInstallerSession.aidl @@ -35,7 +35,7 @@ interface IPackageInstallerSession { void write(String name, long offsetBytes, long lengthBytes, in ParcelFileDescriptor fd); void stageViaHardLink(String target); - void addChecksums(String name, in Checksum[] checksums); + void setChecksums(String name, in Checksum[] checksums, in byte[] signature); void removeSplit(String splitName); diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 01d4c2801b24..248be0f09510 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1247,12 +1247,15 @@ public class PackageInstaller { } /** - * Adds installer-provided checksums for the APK file in session. + * Sets installer-provided checksums for the APK file in session. * * @param name previously written as part of this session. * {@link #openWrite} * @param checksums installer intends to make available via * {@link PackageManager#requestChecksums}. + * @param signature PKCS#7 detached signature bytes over serialized checksums to enable + * fs-verity for the checksums or null if fs-verity should not be enabled. + * @see <a href="https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#built-in-signature-verification">fs-verity</a> * @throws SecurityException if called after the session has been * committed or abandoned. * @throws IllegalStateException if checksums for this file have already been added. @@ -1262,13 +1265,14 @@ public class PackageInstaller { * in {@link PackageManager#requestChecksums}. */ @Deprecated - public void addChecksums(@NonNull String name, @NonNull List<Checksum> checksums) - throws IOException { + public void setChecksums(@NonNull String name, @NonNull List<Checksum> checksums, + @Nullable byte[] signature) throws IOException { Objects.requireNonNull(name); Objects.requireNonNull(checksums); try { - mSession.addChecksums(name, checksums.toArray(new Checksum[checksums.size()])); + mSession.setChecksums(name, checksums.toArray(new Checksum[checksums.size()]), + signature); } catch (RuntimeException e) { ExceptionUtils.maybeUnwrapIOException(e); throw e; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 17c4d25d82d7..03d4d5e10e64 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3513,6 +3513,17 @@ public abstract class PackageManager { public static final String FEATURE_TUNER = "android.hardware.tv.tuner"; /** + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports a enabling/disabling sensor privacy for + * camera. When sensory privacy for the camera is enabled no camera data is send to clients, + * e.g. the view finder in a camera app would appear blank. + * + * @hide + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle"; + + /** * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has * the necessary changes to support app enumeration. * diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index da75fba18f82..522f4ca88519 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -2191,7 +2191,7 @@ public final class ShortcutInfo implements Parcelable { dest.writeString8(mIconUri); } - public static final @android.annotation.NonNull Creator<ShortcutInfo> CREATOR = + public static final @NonNull Creator<ShortcutInfo> CREATOR = new Creator<ShortcutInfo>() { public ShortcutInfo createFromParcel(Parcel source) { return new ShortcutInfo(source); diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java index 056af776a53b..f8fd4a539334 100644 --- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java +++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java @@ -295,8 +295,10 @@ public class PackageInfoWithoutStateUtils { pi.applicationInfo.publicSourceDir = apexFile.getPath(); if (apexInfo.isFactory) { pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; + pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; } else { pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM; + pi.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; } if (apexInfo.isActive) { pi.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED; diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index f7c4c2c5ca15..ec6c23384859 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -881,7 +881,11 @@ public final class Sensor { } } - static int getMaxLengthValuesArray(Sensor sensor, int sdkLevel) { + /** + * Return sensor's maximum length of values array + * @hide + */ + public static int getMaxLengthValuesArray(Sensor sensor, int sdkLevel) { // RotationVector length has changed to 3 to 5 for API level 18 // Set it to 3 for backward compatibility. if (sensor.mType == Sensor.TYPE_ROTATION_VECTOR diff --git a/core/java/android/hardware/input/InputDeviceSensorManager.java b/core/java/android/hardware/input/InputDeviceSensorManager.java index 56c2cddcbf15..89db857b860b 100644 --- a/core/java/android/hardware/input/InputDeviceSensorManager.java +++ b/core/java/android/hardware/input/InputDeviceSensorManager.java @@ -25,6 +25,7 @@ import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.TriggerEventListener; +import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -32,6 +33,7 @@ import android.os.MemoryFile; import android.os.Message; import android.os.RemoteException; import android.util.Slog; +import android.util.SparseArray; import android.view.InputDevice; import com.android.internal.annotations.GuardedBy; @@ -65,8 +67,8 @@ public class InputDeviceSensorManager implements InputManager.InputDeviceListene @GuardedBy("mInputSensorLock") private final ArrayList<InputSensorEventListenerDelegate> mInputSensorEventListeners = new ArrayList<InputSensorEventListenerDelegate>(); - private HandlerThread mSensorThread = null; - private Handler mSensorHandler = null; + private final HandlerThread mSensorThread; + private final Handler mSensorHandler; public InputDeviceSensorManager(InputManager inputManager) { mInputManager = inputManager; @@ -125,9 +127,7 @@ public class InputDeviceSensorManager implements InputManager.InputDeviceListene @Override public void onInputDeviceChanged(int deviceId) { synchronized (mInputSensorLock) { - if (mSensors.containsKey(deviceId)) { - mSensors.remove(deviceId); - } + mSensors.remove(deviceId); updateInputDeviceSensorInfoLocked(deviceId); } } @@ -196,16 +196,21 @@ public class InputDeviceSensorManager implements InputManager.InputDeviceListene + " timestamp=" + timestamp + " sensorType=" + sensorType); } synchronized (mInputSensorLock) { - SensorEvent event = createSensorEvent( - InputDevice.getDevice(deviceId), sensorType, accuracy, timestamp, values); - if (event == null) { - Slog.wtf(TAG, "Failed to create SensorEvent."); - return; - } + Sensor sensor = getInputDeviceSensorLocked(deviceId, sensorType); for (int i = 0; i < mInputSensorEventListeners.size(); i++) { InputSensorEventListenerDelegate listener = mInputSensorEventListeners.get(i); if (listener.hasSensorRegistered(deviceId, sensorType)) { + SensorEvent event = listener.getSensorEvent(sensor); + if (event == null) { + Slog.wtf(TAG, "Failed to get SensorEvent."); + return; + } + event.sensor = sensor; + event.accuracy = accuracy; + event.timestamp = timestamp; + System.arraycopy(values, 0, event.values, 0, event.values.length); + // Call listener for sensor changed listener.sendSensorChanged(event); } } @@ -249,15 +254,19 @@ public class InputDeviceSensorManager implements InputManager.InputDeviceListene private final SensorEventListener mListener; private final int mDelayUs; private final int mMaxBatchReportLatencyUs; + // List of sensors being listened to private List<Sensor> mSensors = new ArrayList<Sensor>(); + // Sensor event array by sensor type, preallocate sensor events for each sensor of listener + // to avoid allocation and garbage collection for each listener callback. + private final SparseArray<SensorEvent> mSensorEvents = new SparseArray<SensorEvent>(); InputSensorEventListenerDelegate(SensorEventListener listener, Sensor sensor, int delayUs, int maxBatchReportLatencyUs, Handler handler) { super(handler != null ? handler.getLooper() : Looper.myLooper()); mListener = listener; - mSensors.add(sensor); mDelayUs = delayUs; mMaxBatchReportLatencyUs = maxBatchReportLatencyUs; + addSensor(sensor); } public List<Sensor> getSensors() { @@ -276,10 +285,12 @@ public class InputDeviceSensorManager implements InputManager.InputDeviceListene // and the sensor list is cleared. if (sensor == null) { mSensors.clear(); + mSensorEvents.clear(); } for (Sensor s : mSensors) { if (sensorEquals(s, sensor)) { mSensors.remove(sensor); + mSensorEvents.remove(sensor.getType()); } } } @@ -295,6 +306,10 @@ public class InputDeviceSensorManager implements InputManager.InputDeviceListene } } mSensors.add(sensor); + final int vecLength = sensor.getMaxLengthValuesArray(sensor, Build.VERSION.SDK_INT); + SensorEvent event = new SensorEvent(sensor, SensorManager.SENSOR_STATUS_NO_CONTACT, + 0 /* timestamp */, new float[vecLength]); + mSensorEvents.put(sensor.getType(), event); } /** @@ -320,6 +335,13 @@ public class InputDeviceSensorManager implements InputManager.InputDeviceListene } /** + * Get SensorEvent object for input device, with specified sensor. + */ + private SensorEvent getSensorEvent(@NonNull Sensor sensor) { + return mSensorEvents.get(sensor.getType()); + } + + /** * Send sensor changed message */ public void sendSensorChanged(SensorEvent event) { @@ -360,26 +382,6 @@ public class InputDeviceSensorManager implements InputManager.InputDeviceListene } /** - * Create SensorEvent object for input device, with specified device ID, sensor Type, - * sensor event timestamp, accuracy, and sensor values. - */ - private SensorEvent createSensorEvent(InputDevice inputDevice, int sensorType, int accuracy, - long timestamp, float[] values) { - synchronized (mInputSensorLock) { - Sensor sensor = getInputDeviceSensorLocked(inputDevice.getId(), sensorType); - if (sensor == null) { - Slog.wtf(TAG, "Can't get sensor type " + sensorType + " for input device " - + inputDevice); - } - SensorEvent event = new SensorEvent(sensor, accuracy, timestamp, values); - if (event == null) { - Slog.wtf(TAG, "Failed to create SensorEvent."); - } - return event; - } - } - - /** * Return the default sensor object for input device, for specific sensor type. */ private Sensor getSensorForInputDevice(int deviceId, int type) { diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 930950e58db3..899af5a08b4d 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -72,7 +72,6 @@ import com.android.internal.util.Protocol; import libcore.net.event.NetworkEventDispatcher; -import java.io.FileDescriptor; import java.io.IOException; import java.io.UncheckedIOException; import java.lang.annotation.Retention; @@ -1958,6 +1957,12 @@ public class ConnectivityManager { return k; } + // Construct an invalid fd. + private ParcelFileDescriptor createInvalidFd() { + final int invalidFd = -1; + return ParcelFileDescriptor.adoptFd(invalidFd); + } + /** * Request that keepalives be started on a IPsec NAT-T socket. * @@ -1988,7 +1993,7 @@ public class ConnectivityManager { } catch (IOException ignored) { // Construct an invalid fd, so that if the user later calls start(), it will fail with // ERROR_INVALID_SOCKET. - dup = new ParcelFileDescriptor(new FileDescriptor()); + dup = createInvalidFd(); } return new NattSocketKeepalive(mService, network, dup, socket.getResourceId(), source, destination, executor, callback); @@ -2030,7 +2035,7 @@ public class ConnectivityManager { } catch (IOException ignored) { // Construct an invalid fd, so that if the user later calls start(), it will fail with // ERROR_INVALID_SOCKET. - dup = new ParcelFileDescriptor(new FileDescriptor()); + dup = createInvalidFd(); } return new NattSocketKeepalive(mService, network, dup, INVALID_RESOURCE_ID /* Unused */, source, destination, executor, callback); @@ -2067,7 +2072,7 @@ public class ConnectivityManager { } catch (UncheckedIOException ignored) { // Construct an invalid fd, so that if the user later calls start(), it will fail with // ERROR_INVALID_SOCKET. - dup = new ParcelFileDescriptor(new FileDescriptor()); + dup = createInvalidFd(); } return new TcpSocketKeepalive(mService, network, dup, executor, callback); } diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index c029deae09df..cad0db257ef6 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -160,7 +160,7 @@ public class NetworkPolicyManager { /** @hide */ public static final int FOREGROUND_THRESHOLD_STATE = - ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; + ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; /** * {@link Intent} extra that indicates which {@link NetworkTemplate} rule it diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java index 58ea91573775..bb91f893c916 100644 --- a/core/java/android/net/metrics/IpConnectivityLog.java +++ b/core/java/android/net/metrics/IpConnectivityLog.java @@ -137,7 +137,7 @@ public class IpConnectivityLog { * @return true if the event was successfully logged. */ public boolean log(@NonNull Network network, @NonNull int[] transports, @NonNull Event data) { - return log(network.netId, transports, data); + return log(network.getNetId(), transports, data); } /** diff --git a/core/java/android/net/vcn/IVcnManagementService.aidl b/core/java/android/net/vcn/IVcnManagementService.aidl index 04b585cdf420..80ac64b87d4d 100644 --- a/core/java/android/net/vcn/IVcnManagementService.aidl +++ b/core/java/android/net/vcn/IVcnManagementService.aidl @@ -16,6 +16,7 @@ package android.net.vcn; +import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; import android.os.ParcelUuid; @@ -25,4 +26,7 @@ import android.os.ParcelUuid; interface IVcnManagementService { void setVcnConfig(in ParcelUuid subscriptionGroup, in VcnConfig config, in String opPkgName); void clearVcnConfig(in ParcelUuid subscriptionGroup); + + void addVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener); + void removeVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener); } diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl b/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl index edf96ddd4e5a..f8ae492016f0 100644 --- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl +++ b/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Android Open Source Project + * 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. @@ -14,17 +14,9 @@ * limitations under the License. */ -package android.media.tv.tunerresourcemanager; +package android.net.vcn; -/** - * Simple container of the FrontendInfo struct defined in the TunerHAL 1.0 interface. - * - * @hide - */ -parcelable TunerFrontendInfo { - int handle; - - int frontendType; - - int exclusiveGroupId; -} +/** @hide */ +interface IVcnUnderlyingNetworkPolicyListener { + void onPolicyChanged(); +}
\ No newline at end of file diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java index 039360a69a3a..d531cdb2a6e9 100644 --- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -15,8 +15,6 @@ */ package android.net.vcn; -import static android.net.NetworkCapabilities.NetCapability; - import static com.android.internal.annotations.VisibleForTesting.Visibility; import android.annotation.IntRange; @@ -233,7 +231,7 @@ public final class VcnGatewayConnectionConfig { * * @param capability the capability to check for */ - public boolean hasExposedCapability(@NetCapability int capability) { + public boolean hasExposedCapability(int capability) { checkValidCapability(capability); return mExposedCapabilities.contains(capability); @@ -254,7 +252,7 @@ public final class VcnGatewayConnectionConfig { * * @param capability the capability to check for */ - public boolean requiresUnderlyingCapability(@NetCapability int capability) { + public boolean requiresUnderlyingCapability(int capability) { checkValidCapability(capability); return mUnderlyingCapabilities.contains(capability); @@ -341,7 +339,7 @@ public final class VcnGatewayConnectionConfig { * @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway * Connection */ - public Builder addExposedCapability(@NetCapability int exposedCapability) { + public Builder addExposedCapability(int exposedCapability) { checkValidCapability(exposedCapability); mExposedCapabilities.add(exposedCapability); @@ -357,7 +355,7 @@ public final class VcnGatewayConnectionConfig { * @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway * Connection */ - public Builder removeExposedCapability(@NetCapability int exposedCapability) { + public Builder removeExposedCapability(int exposedCapability) { checkValidCapability(exposedCapability); mExposedCapabilities.remove(exposedCapability); @@ -373,7 +371,7 @@ public final class VcnGatewayConnectionConfig { * @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying * networks */ - public Builder addRequiredUnderlyingCapability(@NetCapability int underlyingCapability) { + public Builder addRequiredUnderlyingCapability(int underlyingCapability) { checkValidCapability(underlyingCapability); mUnderlyingCapabilities.add(underlyingCapability); @@ -393,7 +391,7 @@ public final class VcnGatewayConnectionConfig { * @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying * networks */ - public Builder removeRequiredUnderlyingCapability(@NetCapability int underlyingCapability) { + public Builder removeRequiredUnderlyingCapability(int underlyingCapability) { checkValidCapability(underlyingCapability); mUnderlyingCapabilities.remove(underlyingCapability); diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index b881a339535b..2ccdc2633af0 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -25,7 +25,12 @@ import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceSpecificException; +import com.android.internal.annotations.VisibleForTesting; + import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; /** * VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks. @@ -60,6 +65,11 @@ import java.io.IOException; public final class VcnManager { @NonNull private static final String TAG = VcnManager.class.getSimpleName(); + @VisibleForTesting + public static final Map< + VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder> + REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>(); + @NonNull private final Context mContext; @NonNull private final IVcnManagementService mService; @@ -136,4 +146,101 @@ public final class VcnManager { throw e.rethrowFromSystemServer(); } } + + // TODO: make VcnUnderlyingNetworkPolicyListener @SystemApi + /** + * VcnUnderlyingNetworkPolicyListener is the interface through which internal system components + * can register to receive updates for VCN-underlying Network policies from the System Server. + * + * @hide + */ + public interface VcnUnderlyingNetworkPolicyListener { + /** + * Notifies the implementation that the VCN's underlying Network policy has changed. + * + * <p>After receiving this callback, implementations MUST poll VcnManager for the updated + * VcnUnderlyingNetworkPolicy via VcnManager#getUnderlyingNetworkPolicy. + */ + void onPolicyChanged(); + } + + /** + * Add a listener for VCN-underlying network policy updates. + * + * @param executor the Executor that will be used for invoking all calls to the specified + * Listener + * @param listener the VcnUnderlyingNetworkPolicyListener to be added + * @throws SecurityException if the caller does not have permission NETWORK_FACTORY + * @throws IllegalArgumentException if the specified VcnUnderlyingNetworkPolicyListener is + * already registered + * @hide + */ + @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) + public void addVcnUnderlyingNetworkPolicyListener( + @NonNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener) { + requireNonNull(executor, "executor must not be null"); + requireNonNull(listener, "listener must not be null"); + + VcnUnderlyingNetworkPolicyListenerBinder binder = + new VcnUnderlyingNetworkPolicyListenerBinder(executor, listener); + if (REGISTERED_POLICY_LISTENERS.putIfAbsent(listener, binder) != null) { + throw new IllegalArgumentException( + "Attempting to add a listener that is already in use"); + } + + try { + mService.addVcnUnderlyingNetworkPolicyListener(binder); + } catch (RemoteException e) { + REGISTERED_POLICY_LISTENERS.remove(listener); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Remove the specified VcnUnderlyingNetworkPolicyListener from VcnManager. + * + * <p>If the specified listener is not currently registered, this is a no-op. + * + * @param listener the VcnUnderlyingNetworkPolicyListener that will be removed + * @hide + */ + public void removeVcnUnderlyingNetworkPolicyListener( + @NonNull VcnUnderlyingNetworkPolicyListener listener) { + requireNonNull(listener, "listener must not be null"); + + VcnUnderlyingNetworkPolicyListenerBinder binder = + REGISTERED_POLICY_LISTENERS.remove(listener); + if (binder == null) { + return; + } + + try { + mService.removeVcnUnderlyingNetworkPolicyListener(binder); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Binder wrapper for added VcnUnderlyingNetworkPolicyListeners to receive signals from System + * Server. + * + * @hide + */ + private static class VcnUnderlyingNetworkPolicyListenerBinder + extends IVcnUnderlyingNetworkPolicyListener.Stub { + @NonNull private final Executor mExecutor; + @NonNull private final VcnUnderlyingNetworkPolicyListener mListener; + + private VcnUnderlyingNetworkPolicyListenerBinder( + Executor executor, VcnUnderlyingNetworkPolicyListener listener) { + mExecutor = executor; + mListener = listener; + } + + @Override + public void onPolicyChanged() { + mExecutor.execute(() -> mListener.onPolicyChanged()); + } + } } diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl new file mode 100644 index 000000000000..6cb6ee685a64 --- /dev/null +++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.vcn; + +/** @hide */ +parcelable VcnUnderlyingNetworkPolicy; diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java new file mode 100644 index 000000000000..dd7c86d87ff2 --- /dev/null +++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.vcn; + +import android.annotation.NonNull; +import android.net.NetworkCapabilities; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * VcnUnderlyingNetworkPolicy represents the Network policy for a VCN-managed Network. + * + * <p>Transports that are bringing up networks capable of acting as a VCN's underlying network + * should query for policy state upon major capability changes (e.g. changing of TRUSTED bit), and + * when prompted by VcnManagementService via VcnUnderlyingNetworkPolicyListener. + * + * @hide + */ +public final class VcnUnderlyingNetworkPolicy implements Parcelable { + private final boolean mIsTearDownRequested; + private final NetworkCapabilities mMergedNetworkCapabilities; + + /** + * Constructs a VcnUnderlyingNetworkPolicy with the specified parameters. + * + * @hide + */ + public VcnUnderlyingNetworkPolicy( + boolean isTearDownRequested, @NonNull NetworkCapabilities mergedNetworkCapabilities) { + Objects.requireNonNull( + mergedNetworkCapabilities, "mergedNetworkCapabilities must be nonnull"); + + mIsTearDownRequested = isTearDownRequested; + mMergedNetworkCapabilities = mergedNetworkCapabilities; + } + + /** + * Returns whether this Carrier VCN policy policy indicates that the underlying Network should + * be torn down. + */ + public boolean isTeardownRequested() { + return mIsTearDownRequested; + } + + /** + * Returns the NetworkCapabilities with Carrier VCN policy bits merged into the provided + * capabilities. + */ + @NonNull + public NetworkCapabilities getMergedNetworkCapabilities() { + return mMergedNetworkCapabilities; + } + + @Override + public int hashCode() { + return Objects.hash(mIsTearDownRequested, mMergedNetworkCapabilities); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VcnUnderlyingNetworkPolicy)) return false; + final VcnUnderlyingNetworkPolicy that = (VcnUnderlyingNetworkPolicy) o; + + return mIsTearDownRequested == that.mIsTearDownRequested + && mMergedNetworkCapabilities.equals(that.mMergedNetworkCapabilities); + } + + /** {@inheritDoc} */ + @Override + public int describeContents() { + return 0; + } + + /** {@inheritDoc} */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeBoolean(mIsTearDownRequested); + dest.writeParcelable(mMergedNetworkCapabilities, flags); + } + + /** Implement the Parcelable interface */ + public static final @NonNull Creator<VcnUnderlyingNetworkPolicy> CREATOR = + new Creator<VcnUnderlyingNetworkPolicy>() { + public VcnUnderlyingNetworkPolicy createFromParcel(Parcel in) { + return new VcnUnderlyingNetworkPolicy( + in.readBoolean(), in.readParcelable(null)); + } + + public VcnUnderlyingNetworkPolicy[] newArray(int size) { + return new VcnUnderlyingNetworkPolicy[size]; + } + }; +} diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java index 33736d3bf9f3..305c686f8657 100644 --- a/core/java/android/os/BugreportManager.java +++ b/core/java/android/os/BugreportManager.java @@ -22,6 +22,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SuppressAutoDoc; import android.annotation.SystemApi; import android.annotation.SystemService; import android.app.ActivityManager; @@ -40,12 +41,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.concurrent.Executor; -/** - * Class that provides a privileged API to capture and consume bugreports. - * - * @hide - */ -@SystemApi +/** Class that provides a privileged API to capture and consume bugreports. */ @SystemService(Context.BUGREPORT_SERVICE) public final class BugreportManager { @@ -60,28 +56,30 @@ public final class BugreportManager { mBinder = binder; } - /** - * An interface describing the callback for bugreport progress and status. - */ + /** An interface describing the callback for bugreport progress and status. */ public abstract static class BugreportCallback { - /** @hide */ + /** + * Possible error codes taking a bugreport can encounter. + * + * @hide + */ @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = { "BUGREPORT_ERROR_" }, value = { - BUGREPORT_ERROR_INVALID_INPUT, - BUGREPORT_ERROR_RUNTIME, - BUGREPORT_ERROR_USER_DENIED_CONSENT, - BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT, - BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS - }) - - /** Possible error codes taking a bugreport can encounter */ + @IntDef( + prefix = {"BUGREPORT_ERROR_"}, + value = { + BUGREPORT_ERROR_INVALID_INPUT, + BUGREPORT_ERROR_RUNTIME, + BUGREPORT_ERROR_USER_DENIED_CONSENT, + BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT, + BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS + }) public @interface BugreportErrorCode {} /** The input options were invalid */ public static final int BUGREPORT_ERROR_INVALID_INPUT = IDumpstateListener.BUGREPORT_ERROR_INVALID_INPUT; - /** A runtime error occured */ + /** A runtime error occurred */ public static final int BUGREPORT_ERROR_RUNTIME = IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR; @@ -99,6 +97,7 @@ public final class BugreportManager { /** * Called when there is a progress update. + * * @param progress the progress in [0.0, 100.0] */ public void onProgress(@FloatRange(from = 0f, to = 100f) float progress) {} @@ -113,14 +112,12 @@ public final class BugreportManager { * out, but the bugreport could be available in the internal directory of dumpstate for * manual retrieval. * - * <p> If {@code BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS} is passed, then the - * caller should try later, as only one bugreport can be in progress at a time. + * <p>If {@code BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS} is passed, then the caller + * should try later, as only one bugreport can be in progress at a time. */ public void onError(@BugreportErrorCode int errorCode) {} - /** - * Called when taking bugreport finishes successfully. - */ + /** Called when taking bugreport finishes successfully. */ public void onFinished() {} /** @@ -137,20 +134,23 @@ public final class BugreportManager { * seconds to return in the worst case. {@code callback} will receive progress and status * updates. * - * <p>The bugreport artifacts will be copied over to the given file descriptors only if the - * user consents to sharing with the calling app. + * <p>The bugreport artifacts will be copied over to the given file descriptors only if the user + * consents to sharing with the calling app. * * <p>{@link BugreportManager} takes ownership of {@code bugreportFd} and {@code screenshotFd}. * - * @param bugreportFd file to write the bugreport. This should be opened in write-only, - * append mode. - * @param screenshotFd file to write the screenshot, if necessary. This should be opened - * in write-only, append mode. + * @param bugreportFd file to write the bugreport. This should be opened in write-only, append + * mode. + * @param screenshotFd file to write the screenshot, if necessary. This should be opened in + * write-only, append mode. * @param params options that specify what kind of a bugreport should be taken * @param callback callback for progress and status updates + * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.DUMP) - public void startBugreport(@NonNull ParcelFileDescriptor bugreportFd, + public void startBugreport( + @NonNull ParcelFileDescriptor bugreportFd, @Nullable ParcelFileDescriptor screenshotFd, @NonNull BugreportParams params, @NonNull @CallbackExecutor Executor executor, @@ -164,17 +164,21 @@ public final class BugreportManager { boolean isScreenshotRequested = screenshotFd != null; if (screenshotFd == null) { // Binder needs a valid File Descriptor to be passed - screenshotFd = ParcelFileDescriptor.open(new File("/dev/null"), - ParcelFileDescriptor.MODE_READ_ONLY); + screenshotFd = + ParcelFileDescriptor.open( + new File("/dev/null"), ParcelFileDescriptor.MODE_READ_ONLY); } - DumpstateListener dsListener = new DumpstateListener(executor, callback, - isScreenshotRequested); + DumpstateListener dsListener = + new DumpstateListener(executor, callback, isScreenshotRequested); // Note: mBinder can get callingUid from the binder transaction. - mBinder.startBugreport(-1 /* callingUid */, + mBinder.startBugreport( + -1 /* callingUid */, mContext.getOpPackageName(), bugreportFd.getFileDescriptor(), screenshotFd.getFileDescriptor(), - params.getMode(), dsListener, isScreenshotRequested); + params.getMode(), + dsListener, + isScreenshotRequested); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (FileNotFoundException e) { @@ -189,14 +193,60 @@ public final class BugreportManager { } /** + * Starts a connectivity bugreport. + * + * <p>The connectivity bugreport is a specialized version of bugreport that only includes + * information specifically for debugging connectivity-related issues (e.g. telephony, wi-fi, + * and IP networking issues). It is intended primarily for use by OEMs and network providers + * such as mobile network operators. In addition to generally excluding information that isn't + * targeted to connectivity debugging, this type of bugreport excludes PII and sensitive + * information that isn't strictly necessary for connectivity debugging. + * + * <p>The calling app MUST have a context-specific reason for requesting a connectivity + * bugreport, such as detecting a connectivity-related issue. This API SHALL NOT be used to + * perform random sampling from a fleet of public end-user devices. + * + * <p>Calling this API will cause the system to ask the user for consent every single time. The + * bugreport artifacts will be copied over to the given file descriptors only if the user + * consents to sharing with the calling app. + * + * <p>This starts a bugreport in the background. However the call itself can take several + * seconds to return in the worst case. {@code callback} will receive progress and status + * updates. + * + * <p>Requires that the calling app has carrier privileges (see {@link + * android.telephony.TelephonyManager#hasCarrierPrivileges}) on any active subscription. + * + * @param bugreportFd file to write the bugreport. This should be opened in write-only, append + * mode. + * @param callback callback for progress and status updates. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + public void startConnectivityBugreport( + @NonNull ParcelFileDescriptor bugreportFd, + @NonNull @CallbackExecutor Executor executor, + @NonNull BugreportCallback callback) { + startBugreport( + bugreportFd, + null /* screenshotFd */, + new BugreportParams(BugreportParams.BUGREPORT_MODE_TELEPHONY), + executor, + callback); + } + + /** * Cancels the currently running bugreport. * * <p>Apps are only able to cancel their own bugreports. App A cannot cancel a bugreport started * by app B. * + * <p>Requires permission: {@link android.Manifest.permission#DUMP} or that the calling app has + * carrier privileges (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on + * any active subscription. + * * @throws SecurityException if trying to cancel another app's bugreport in progress */ - @RequiresPermission(android.Manifest.permission.DUMP) + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges public void cancelBugreport() { try { mBinder.cancelBugreport(-1 /* callingUid */, mContext.getOpPackageName()); @@ -209,23 +259,26 @@ public final class BugreportManager { * Requests a bugreport. * * <p>This requests the platform/system to take a bugreport and makes the final bugreport - * available to the user. The user may choose to share it with another app, but the bugreport - * is never given back directly to the app that requested it. + * available to the user. The user may choose to share it with another app, but the bugreport is + * never given back directly to the app that requested it. * - * @param params {@link BugreportParams} that specify what kind of a bugreport should - * be taken, please note that not all kinds of bugreport allow for a - * progress notification - * @param shareTitle title on the final share notification + * @param params {@link BugreportParams} that specify what kind of a bugreport should be taken, + * please note that not all kinds of bugreport allow for a progress notification + * @param shareTitle title on the final share notification * @param shareDescription description on the final share notification + * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.DUMP) - public void requestBugreport(@NonNull BugreportParams params, @Nullable CharSequence shareTitle, + public void requestBugreport( + @NonNull BugreportParams params, + @Nullable CharSequence shareTitle, @Nullable CharSequence shareDescription) { try { String title = shareTitle == null ? null : shareTitle.toString(); String description = shareDescription == null ? null : shareDescription.toString(); - ActivityManager.getService().requestBugReportWithDescription(title, description, - params.getMode()); + ActivityManager.getService() + .requestBugReportWithDescription(title, description, params.getMode()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -236,8 +289,8 @@ public final class BugreportManager { private final BugreportCallback mCallback; private final boolean mIsScreenshotRequested; - DumpstateListener(Executor executor, BugreportCallback callback, - boolean isScreenshotRequested) { + DumpstateListener( + Executor executor, BugreportCallback callback, boolean isScreenshotRequested) { mExecutor = executor; mCallback = callback; mIsScreenshotRequested = isScreenshotRequested; @@ -247,9 +300,7 @@ public final class BugreportManager { public void onProgress(int progress) throws RemoteException { final long identity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> { - mCallback.onProgress(progress); - }); + mExecutor.execute(() -> mCallback.onProgress(progress)); } finally { Binder.restoreCallingIdentity(identity); } @@ -259,9 +310,7 @@ public final class BugreportManager { public void onError(int errorCode) throws RemoteException { final long identity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> { - mCallback.onError(errorCode); - }); + mExecutor.execute(() -> mCallback.onError(errorCode)); } finally { Binder.restoreCallingIdentity(identity); } @@ -271,9 +320,7 @@ public final class BugreportManager { public void onFinished() throws RemoteException { final long identity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> { - mCallback.onFinished(); - }); + mExecutor.execute(() -> mCallback.onFinished()); } finally { Binder.restoreCallingIdentity(identity); } @@ -288,20 +335,19 @@ public final class BugreportManager { Handler mainThreadHandler = new Handler(Looper.getMainLooper()); mainThreadHandler.post( () -> { - int message = success ? R.string.bugreport_screenshot_success_toast - : R.string.bugreport_screenshot_failure_toast; + int message = + success + ? R.string.bugreport_screenshot_success_toast + : R.string.bugreport_screenshot_failure_toast; Toast.makeText(mContext, message, Toast.LENGTH_LONG).show(); }); } @Override - public void onUiIntensiveBugreportDumpsFinished() - throws RemoteException { + public void onUiIntensiveBugreportDumpsFinished() throws RemoteException { final long identity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> { - mCallback.onEarlyReportFinished(); - }); + mExecutor.execute(() -> mCallback.onEarlyReportFinished()); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/core/java/android/os/CombinedVibrationEffect.java b/core/java/android/os/CombinedVibrationEffect.java index 7ec7fffd3588..869a72717f9f 100644 --- a/core/java/android/os/CombinedVibrationEffect.java +++ b/core/java/android/os/CombinedVibrationEffect.java @@ -87,8 +87,14 @@ public abstract class CombinedVibrationEffect implements Parcelable { } /** @hide */ + public abstract long getDuration(); + + /** @hide */ public abstract void validate(); + /** @hide */ + public abstract boolean hasVibrator(int vibratorId); + /** * A combination of haptic effects that should be played in multiple vibrators in sync. * @@ -265,6 +271,11 @@ public abstract class CombinedVibrationEffect implements Parcelable { return mEffect; } + @Override + public long getDuration() { + return mEffect.getDuration(); + } + /** @hide */ @Override public void validate() { @@ -272,12 +283,17 @@ public abstract class CombinedVibrationEffect implements Parcelable { } @Override + public boolean hasVibrator(int vibratorId) { + return true; + } + + @Override public boolean equals(Object o) { if (!(o instanceof Mono)) { return false; } Mono other = (Mono) o; - return other.mEffect.equals(other.mEffect); + return mEffect.equals(other.mEffect); } @Override @@ -345,6 +361,15 @@ public abstract class CombinedVibrationEffect implements Parcelable { return mEffects; } + @Override + public long getDuration() { + long maxDuration = Long.MIN_VALUE; + for (int i = 0; i < mEffects.size(); i++) { + maxDuration = Math.max(maxDuration, mEffects.valueAt(i).getDuration()); + } + return maxDuration; + } + /** @hide */ @Override public void validate() { @@ -356,6 +381,11 @@ public abstract class CombinedVibrationEffect implements Parcelable { } @Override + public boolean hasVibrator(int vibratorId) { + return mEffects.indexOfKey(vibratorId) >= 0; + } + + @Override public boolean equals(Object o) { if (!(o instanceof Stereo)) { return false; @@ -445,6 +475,26 @@ public abstract class CombinedVibrationEffect implements Parcelable { return mDelays; } + @Override + public long getDuration() { + long durations = 0; + final int effectCount = mEffects.size(); + for (int i = 0; i < effectCount; i++) { + CombinedVibrationEffect effect = mEffects.get(i); + long duration = effect.getDuration(); + if (duration < 0) { + // If any duration is unknown, this combination duration is also unknown. + return duration; + } + durations += duration; + } + long delays = 0; + for (int i = 0; i < effectCount; i++) { + delays += mDelays.get(i); + } + return durations + delays; + } + /** @hide */ @Override public void validate() { @@ -452,13 +502,15 @@ public abstract class CombinedVibrationEffect implements Parcelable { "There should be at least one effect set for a combined effect"); Preconditions.checkArgument(mEffects.size() == mDelays.size(), "Effect and delays should have equal length"); - for (long delay : mDelays) { - if (delay < 0) { + final int effectCount = mEffects.size(); + for (int i = 0; i < effectCount; i++) { + if (mDelays.get(i) < 0) { throw new IllegalArgumentException("Delays must all be >= 0" + " (delays=" + mDelays + ")"); } } - for (CombinedVibrationEffect effect : mEffects) { + for (int i = 0; i < effectCount; i++) { + CombinedVibrationEffect effect = mEffects.get(i); if (effect instanceof Sequential) { throw new IllegalArgumentException( "There should be no nested sequential effects in a combined effect"); @@ -468,6 +520,17 @@ public abstract class CombinedVibrationEffect implements Parcelable { } @Override + public boolean hasVibrator(int vibratorId) { + final int effectCount = mEffects.size(); + for (int i = 0; i < effectCount; i++) { + if (mEffects.get(i).hasVibrator(vibratorId)) { + return true; + } + } + return false; + } + + @Override public boolean equals(Object o) { if (!(o instanceof Sequential)) { return false; diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 0326b72ece7c..a46af9754f32 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -1442,11 +1442,13 @@ public final class FileUtils { public static FileDescriptor convertToModernFd(FileDescriptor fd) { try { Context context = AppGlobals.getInitialApplication(); - File realFile = ParcelFileDescriptor.getFile(fd); + // /mnt/user paths are not accessible directly so convert to a /storage path + String filePath = Os.readlink("/proc/self/fd/" + fd.getInt$()).replace( + "/mnt/user/" + UserHandle.myUserId(), "/storage"); + File realFile = new File(filePath); String fileName = realFile.getName(); boolean isCameraVideo = !fileName.startsWith(".") && fileName.endsWith(".mp4") - && contains(CAMERA_DIR_LOWER_CASE, realFile.getAbsolutePath().toLowerCase( - Locale.ROOT)); + && contains(CAMERA_DIR_LOWER_CASE, filePath.toLowerCase(Locale.ROOT)); if (!SystemProperties.getBoolean("sys.fuse.transcode_enabled", false) || UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context) @@ -1471,7 +1473,7 @@ public final class FileUtils { Log.i(TAG, "Failed to change to modern format dataSource for: " + realFile); } } catch (Exception e) { - Log.w(TAG, "Failed to change to modern format dataSource"); + Log.w(TAG, "Failed to change to modern format dataSource", e); } return null; } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index b951aca6d680..814a248cbb04 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -612,12 +612,14 @@ public final class PowerManager { * @hide */ public static class WakeData { - public WakeData(long wakeTime, @WakeReason int wakeReason) { + public WakeData(long wakeTime, @WakeReason int wakeReason, long sleepDuration) { this.wakeTime = wakeTime; this.wakeReason = wakeReason; + this.sleepDuration = sleepDuration; } public long wakeTime; public @WakeReason int wakeReason; + public long sleepDuration; } /** diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 086180e7ead4..b13be9f4a91a 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -2194,6 +2194,33 @@ public final class StrictMode { onVmPolicyViolation(new IncorrectContextUseViolation(message, originStack)); } + /** + * A helper method to verify if the {@code context} is a UI context and throw + * {@link IncorrectContextUseViolation} if the {@code context} is not a UI context. + * + * @param context The context to verify if it is a UI context + * @param methodName The asserted method name + * + * @see Context#isUiContext() + * @see IncorrectContextUseViolation + * + * @hide + */ + public static void assertUiContext(@NonNull Context context, @NonNull String methodName) { + if (vmIncorrectContextUseEnabled() && !context.isUiContext()) { + final String errorMessage = "Tried to access UI related API" + methodName + + " from a non-UI Context:" + context; + final String message = "UI-related services, such as WindowManager, WallpaperService " + + "or LayoutInflater should be accessed from Activity or other UI " + + "Contexts. Use an Activity or a Context created with " + + "Context#createWindowContext(int, Bundle), which are adjusted to " + + "the configuration and visual bounds of an area on screen."; + final Exception exception = new IllegalAccessException(errorMessage); + StrictMode.onIncorrectContextUsed(message, exception); + Log.e(TAG, errorMessage + " " + message, exception); + } + } + /** Assume locked until we hear otherwise */ private static volatile boolean sUserKeyUnlocked = false; diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index c0b2ada7860c..df3beb2c6ea7 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -317,7 +317,7 @@ public abstract class VibrationEffect implements Parcelable { */ @TestApi public static VibrationEffect get(int effectId, boolean fallback) { - VibrationEffect effect = new Prebaked(effectId, fallback); + VibrationEffect effect = new Prebaked(effectId, fallback, EffectStrength.MEDIUM); effect.validate(); return effect; } @@ -792,22 +792,30 @@ public abstract class VibrationEffect implements Parcelable { public static class Prebaked extends VibrationEffect implements Parcelable { private final int mEffectId; private final boolean mFallback; - - private int mEffectStrength; + private final int mEffectStrength; + @Nullable + private final VibrationEffect mFallbackEffect; public Prebaked(Parcel in) { - this(in.readInt(), in.readByte() != 0, in.readInt()); + mEffectId = in.readInt(); + mFallback = in.readByte() != 0; + mEffectStrength = in.readInt(); + mFallbackEffect = in.readParcelable(VibrationEffect.class.getClassLoader()); } - public Prebaked(int effectId, boolean fallback) { - this(effectId, fallback, EffectStrength.MEDIUM); + public Prebaked(int effectId, boolean fallback, int effectStrength) { + mEffectId = effectId; + mFallback = fallback; + mEffectStrength = effectStrength; + mFallbackEffect = null; } /** @hide */ - public Prebaked(int effectId, boolean fallback, int effectStrength) { + public Prebaked(int effectId, int effectStrength, @NonNull VibrationEffect fallbackEffect) { mEffectId = effectId; - mFallback = fallback; + mFallback = true; mEffectStrength = effectStrength; + mFallbackEffect = fallbackEffect; } public int getId() { @@ -829,33 +837,44 @@ public abstract class VibrationEffect implements Parcelable { /** @hide */ @Override - public VibrationEffect resolve(int defaultAmplitude) { - // Prebaked effects already have default amplitude set, so ignore this. + public Prebaked resolve(int defaultAmplitude) { + if (mFallbackEffect != null) { + VibrationEffect resolvedFallback = mFallbackEffect.resolve(defaultAmplitude); + if (!mFallbackEffect.equals(resolvedFallback)) { + return new Prebaked(mEffectId, mEffectStrength, resolvedFallback); + } + } return this; } /** @hide */ @Override public Prebaked scale(float scaleFactor) { - // Prebaked effects cannot be scaled, so ignore this. + if (mFallbackEffect != null) { + VibrationEffect scaledFallback = mFallbackEffect.scale(scaleFactor); + if (!mFallbackEffect.equals(scaledFallback)) { + return new Prebaked(mEffectId, mEffectStrength, scaledFallback); + } + } + // Prebaked effect strength cannot be scaled with this method. return this; } /** - * Set the effect strength of the prebaked effect. + * Set the effect strength. */ - public void setEffectStrength(int strength) { - if (!isValidEffectStrength(strength)) { - throw new IllegalArgumentException("Invalid effect strength: " + strength); - } - mEffectStrength = strength; + public int getEffectStrength() { + return mEffectStrength; } /** - * Set the effect strength. + * Return the fallback effect, if set. + * + * @hide */ - public int getEffectStrength() { - return mEffectStrength; + @Nullable + public VibrationEffect getFallbackEffect() { + return mFallbackEffect; } private static boolean isValidEffectStrength(int strength) { @@ -901,15 +920,13 @@ public abstract class VibrationEffect implements Parcelable { VibrationEffect.Prebaked other = (VibrationEffect.Prebaked) o; return mEffectId == other.mEffectId && mFallback == other.mFallback - && mEffectStrength == other.mEffectStrength; + && mEffectStrength == other.mEffectStrength + && Objects.equals(mFallbackEffect, other.mFallbackEffect); } @Override public int hashCode() { - int result = 17; - result += 37 * mEffectId; - result += 37 * mEffectStrength; - return result; + return Objects.hash(mEffectId, mFallback, mEffectStrength, mFallbackEffect); } @Override @@ -917,6 +934,7 @@ public abstract class VibrationEffect implements Parcelable { return "Prebaked{mEffectId=" + mEffectId + ", mEffectStrength=" + mEffectStrength + ", mFallback=" + mFallback + + ", mFallbackEffect=" + mFallbackEffect + "}"; } @@ -927,6 +945,7 @@ public abstract class VibrationEffect implements Parcelable { out.writeInt(mEffectId); out.writeByte((byte) (mFallback ? 1 : 0)); out.writeInt(mEffectStrength); + out.writeParcelable(mFallbackEffect, flags); } public static final @NonNull Parcelable.Creator<Prebaked> CREATOR = @@ -990,8 +1009,10 @@ public abstract class VibrationEffect implements Parcelable { // Just return this if there's no scaling to be done. return this; } + final int primitiveCount = mPrimitiveEffects.size(); List<Composition.PrimitiveEffect> scaledPrimitives = new ArrayList<>(); - for (Composition.PrimitiveEffect primitive : mPrimitiveEffects) { + for (int i = 0; i < primitiveCount; i++) { + Composition.PrimitiveEffect primitive = mPrimitiveEffects.get(i); scaledPrimitives.add(new Composition.PrimitiveEffect( primitive.id, scale(primitive.scale, scaleFactor), primitive.delay)); } @@ -1001,11 +1022,12 @@ public abstract class VibrationEffect implements Parcelable { /** @hide */ @Override public void validate() { - for (Composition.PrimitiveEffect effect : mPrimitiveEffects) { - Composition.checkPrimitive(effect.id); - Preconditions.checkArgumentInRange( - effect.scale, 0.0f, 1.0f, "scale"); - Preconditions.checkArgumentNonNegative(effect.delay, + final int primitiveCount = mPrimitiveEffects.size(); + for (int i = 0; i < primitiveCount; i++) { + Composition.PrimitiveEffect primitive = mPrimitiveEffects.get(i); + Composition.checkPrimitive(primitive.id); + Preconditions.checkArgumentInRange(primitive.scale, 0.0f, 1.0f, "scale"); + Preconditions.checkArgumentNonNegative(primitive.delay, "Primitive delay must be zero or positive"); } } diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java index 59292baa110c..9fdc72bbe6c6 100644 --- a/core/java/android/os/incremental/IncrementalFileStorages.java +++ b/core/java/android/os/incremental/IncrementalFileStorages.java @@ -36,6 +36,7 @@ import android.annotation.Nullable; import android.content.Context; import android.content.pm.DataLoaderParams; import android.content.pm.IDataLoaderStatusListener; +import android.content.pm.IPackageLoadingProgressCallback; import android.content.pm.InstallationFileParcel; import android.text.TextUtils; @@ -70,7 +71,8 @@ public final class IncrementalFileStorages { @Nullable StorageHealthCheckParams healthCheckParams, @Nullable IStorageHealthListener healthListener, @NonNull List<InstallationFileParcel> addedFiles, - @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException { + @NonNull PerUidReadTimeouts[] perUidReadTimeouts, + IPackageLoadingProgressCallback progressCallback) throws IOException { // TODO(b/136132412): validity check if session should not be incremental IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService( Context.INCREMENTAL_SERVICE); @@ -95,7 +97,11 @@ public final class IncrementalFileStorages { throw new IOException("Unknown file location: " + file.location); } } - + // Register progress loading callback after files have been added + if (progressCallback != null) { + incrementalManager.registerLoadingProgressCallback(stageDir.getAbsolutePath(), + progressCallback); + } result.startLoading(); return result; @@ -180,6 +186,7 @@ public final class IncrementalFileStorages { try { mDefaultStorage.unBind(mStageDir.getAbsolutePath()); + mDefaultStorage.unregisterLoadingProgressListener(); } catch (IOException ignored) { } mDefaultStorage = null; diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java index 67317c7b2224..d32928cbeb38 100644 --- a/core/java/android/os/storage/DiskInfo.java +++ b/core/java/android/os/storage/DiskInfo.java @@ -50,6 +50,8 @@ public class DiskInfo implements Parcelable { public static final int FLAG_DEFAULT_PRIMARY = 1 << 1; public static final int FLAG_SD = 1 << 2; public static final int FLAG_USB = 1 << 3; + /** The FLAG_STUB_VISIBLE is set from vold, which gets the flag from outside (e.g., ChromeOS) */ + public static final int FLAG_STUB_VISIBLE = 1 << 6; public final String id; @UnsupportedAppUsage @@ -152,6 +154,10 @@ public class DiskInfo implements Parcelable { return (flags & FLAG_USB) != 0; } + public boolean isStubVisible() { + return (flags & FLAG_STUB_VISIBLE) != 0; + } + @Override public String toString() { final CharArrayWriter writer = new CharArrayWriter(); diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl index 99bdfd1fc103..4669b208b163 100644 --- a/core/java/android/os/storage/IStorageManager.aidl +++ b/core/java/android/os/storage/IStorageManager.aidl @@ -195,4 +195,5 @@ interface IStorageManager { void abortChanges(in String message, boolean retry) = 87; void clearUserKeyAuth(int userId, int serialNumber, in byte[] token, in byte[] secret) = 88; void fixupAppDir(in String path) = 89; + void disableAppDataIsolation(in String pkgName, int pid, int userId) = 90; } diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java index e5997764ce43..c28b59ba9aac 100644 --- a/core/java/android/permission/PermissionUsageHelper.java +++ b/core/java/android/permission/PermissionUsageHelper.java @@ -183,8 +183,9 @@ public class PermissionUsageHelper { } Map<String, List<OpUsage>> rawUsages = getOpUsages(ops); + Set<List<PackageAttribution>> proxyChains = getProxyChains(rawUsages.get(MICROPHONE)); Map<PackageAttribution, CharSequence> packagesWithAttributionLabels = - getTrustedAttributions(rawUsages.get(MICROPHONE)); + getTrustedAttributions(rawUsages.get(MICROPHONE), proxyChains); List<String> usedPermGroups = new ArrayList<>(rawUsages.keySet()); for (int permGroupNum = 0; permGroupNum < usedPermGroups.size(); permGroupNum++) { @@ -192,10 +193,14 @@ public class PermissionUsageHelper { String permGroup = usedPermGroups.get(permGroupNum); Map<PackageAttribution, CharSequence> pkgAttrLabels = packagesWithAttributionLabels; + Set<List<PackageAttribution>> proxies = proxyChains; if (!MICROPHONE.equals(permGroup)) { pkgAttrLabels = new ArrayMap<>(); + proxies = new ArraySet<>(); } - removeDuplicates(rawUsages.get(permGroup), pkgAttrLabels.keySet()); + + List<OpUsage> permUsages = removeDuplicatesAndProxies(rawUsages.get(permGroup), + pkgAttrLabels.keySet(), proxies); if (permGroup.equals(OPSTR_PHONE_CALL_MICROPHONE)) { isPhone = true; @@ -205,9 +210,8 @@ public class PermissionUsageHelper { permGroup = CAMERA; } - int numUsages = rawUsages.get(permGroup).size(); - for (int usageNum = 0; usageNum < numUsages; usageNum++) { - OpUsage usage = rawUsages.get(permGroup).get(usageNum); + for (int usageNum = 0; usageNum < permUsages.size(); usageNum++) { + OpUsage usage = permUsages.get(usageNum); usages.add(new PermGroupUsage(usage.packageName, usage.uid, permGroup, usage.lastAccessTime, usage.isRunning, isPhone, packagesWithAttributionLabels.get(usage.toPackageAttr()))); @@ -278,7 +282,7 @@ public class PermissionUsageHelper { AppOpsManager.OpEventProxyInfo proxy = attrOpEntry.getLastProxyInfo(opFlags); if (proxy != null && proxy.getPackageName() != null) { proxyUsage = new OpUsage(proxy.getPackageName(), proxy.getAttributionTag(), - uid, lastAccessTime, isRunning, null); + proxy.getUid(), lastAccessTime, isRunning, null); } String permGroupName = getGroupForOp(op); @@ -323,17 +327,15 @@ public class PermissionUsageHelper { * trusted attribution label, if there is one */ private ArrayMap<PackageAttribution, CharSequence> getTrustedAttributions( - List<OpUsage> usages) { + List<OpUsage> usages, Set<List<PackageAttribution>> proxyChains) { ArrayMap<PackageAttribution, CharSequence> attributions = new ArrayMap<>(); if (usages == null) { return attributions; } - Set<List<PackageAttribution>> proxyChains = getProxyChains(usages); Map<PackageAttribution, CharSequence> trustedLabels = getTrustedAttributionLabels(usages); - for (List<PackageAttribution> chain : proxyChains) { // If this chain is empty, or has only one link, then do not show any special labels if (chain.size() <= 1) { @@ -413,37 +415,24 @@ public class PermissionUsageHelper { * the chain has the previous one listed as a proxy usage. */ private Set<List<PackageAttribution>> getProxyChains(List<OpUsage> usages) { - ArrayMap<PackageAttribution, List<PackageAttribution>> proxyChains = new ArrayMap<>(); + if (usages == null) { + return new ArraySet<>(); + } + + ArrayMap<PackageAttribution, ArrayList<PackageAttribution>> proxyChains = new ArrayMap<>(); // map of usages that still need to be removed, or added to a chain ArrayMap<PackageAttribution, OpUsage> remainingUsages = new ArrayMap<>(); // map of usage.proxy -> usage, telling us if a usage is a proxy ArrayMap<PackageAttribution, PackageAttribution> proxies = new ArrayMap<>(); - for (int i = 0; i < remainingUsages.size(); i++) { + for (int i = 0; i < usages.size(); i++) { OpUsage usage = usages.get(i); remainingUsages.put(usage.toPackageAttr(), usage); if (usage.proxy != null) { proxies.put(usage.proxy.toPackageAttr(), usage.toPackageAttr()); } } - // find and remove all one-link chains (that is, all proxied apps whose proxy is not - // included in the usage list), and apps that are neither proxy nor proxied. - for (int usageNum = 0; usageNum < usages.size(); usageNum++) { - OpUsage usage = usages.get(usageNum); - PackageAttribution usageAttr = usage.toPackageAttr(); - if (usage.proxy == null) { - if (!proxies.containsKey(usageAttr)) { - remainingUsages.remove(usageAttr); - } - continue; - } - - PackageAttribution proxyAttr = usage.proxy.toPackageAttr(); - if (!remainingUsages.containsKey(proxyAttr)) { - remainingUsages.remove(usageAttr); - } - } - // find all possible starting points for chains + // find all possible end points for chains List<PackageAttribution> keys = new ArrayList<>(remainingUsages.keySet()); for (int usageNum = 0; usageNum < remainingUsages.size(); usageNum++) { OpUsage usage = remainingUsages.get(keys.get(usageNum)); @@ -451,16 +440,21 @@ public class PermissionUsageHelper { continue; } PackageAttribution usageAttr = usage.toPackageAttr(); - // If this usage has a proxy, but is not a proxy, it is the start of a chain. + // If this usage has a proxy, but is not a proxy, it is the end of a chain. + // If it has no proxy, and isn't a proxy, remove it. if (!proxies.containsKey(usageAttr) && usage.proxy != null) { - proxyChains.put(usageAttr, List.of(usageAttr)); + ArrayList<PackageAttribution> proxyList = new ArrayList<>(); + proxyList.add(usageAttr); + proxyChains.put(usageAttr, proxyList); + } else if (!proxies.containsKey(usageAttr) && usage.proxy == null) { + remainingUsages.remove(keys.get(usageNum)); } } - // assemble the chains + // assemble the chains in reverse order, then invert them for (int numStart = 0; numStart < proxyChains.size(); numStart++) { PackageAttribution currPackageAttr = proxyChains.keyAt(numStart); - List<PackageAttribution> proxyChain = proxyChains.get(currPackageAttr); + ArrayList<PackageAttribution> proxyChain = proxyChains.get(currPackageAttr); OpUsage currentUsage = remainingUsages.get(currPackageAttr); if (currentUsage == null || proxyChain == null) { continue; @@ -484,6 +478,8 @@ public class PermissionUsageHelper { proxyChain.add(currPackageAttr); } + // invert the lists, so the element without a proxy is first on the list + Collections.reverse(proxyChain); } return new ArraySet<>(proxyChains.values()); @@ -590,7 +586,7 @@ public class PermissionUsageHelper { CharSequence label = getAttributionLabel(usage); if (trustedMap.get(usage.packageName).equals(label)) { - toSetMap.put(usage.toPackageAttr(), label); + toSetMap.put(opUsage.toPackageAttr(), label); } } @@ -619,34 +615,76 @@ public class PermissionUsageHelper { } } - private void removeDuplicates(List<OpUsage> rawUsages, - Set<PackageAttribution> specialAttributions) { - List<OpUsage> toRemove = new ArrayList<>(); + /** + * If we have multiple usages of a + * @param rawUsages The list of all usages that we wish to + * @param specialAttributions A set of all usages that have a special label + * @param proxies A list of proxy chains- all links but the last on the chain should be removed, + * if the last link has a special label + * @return A list of usages without duplicates or proxy usages. + */ + private List<OpUsage> removeDuplicatesAndProxies(List<OpUsage> rawUsages, + Set<PackageAttribution> specialAttributions, + Set<List<PackageAttribution>> proxies) { + List<OpUsage> deDuped = new ArrayList<>(); if (rawUsages == null) { - return; + return deDuped; + } + + List<PackageAttribution> toRemoveProxies = new ArrayList<>(); + for (List<PackageAttribution> proxyList: proxies) { + PackageAttribution lastLink = proxyList.get(proxyList.size() - 1); + if (!specialAttributions.contains(lastLink)) { + continue; + } + for (int proxyNum = 0; proxyNum < proxyList.size(); proxyNum++) { + if (!proxyList.get(proxyNum).equals(lastLink)) { + toRemoveProxies.add(proxyList.get(proxyNum)); + } + } } for (int usageNum = 0; usageNum < rawUsages.size(); usageNum++) { - PackageAttribution usageAttr = rawUsages.get(usageNum).toPackageAttr(); + OpUsage usage = rawUsages.get(usageNum); + // If this attribution has a special attribution, do not remove it - if (specialAttributions.contains(usageAttr)) { + if (specialAttributions.contains(usage.toPackageAttr())) { + deDuped.add(usage); + } + + // If this attribution is a proxy, remove it + if (toRemoveProxies.contains(usage.toPackageAttr())) { continue; } - // Search the rest of the list for apps with the same uid. If there is one, mark this - // usage for removal. - for (int otherUsageNum = usageNum + 1; otherUsageNum < rawUsages.size(); - otherUsageNum++) { - if (rawUsages.get(otherUsageNum).uid == usageAttr.uid) { - toRemove.add(rawUsages.get(usageNum)); - break; + + // Search the rest of the list for usages with the same UID. If this is the most recent + // usage for that uid, keep it. Otherwise, remove it + boolean isMostRecentForUid = true; + for (int otherUsageNum = 0; otherUsageNum < rawUsages.size(); otherUsageNum++) { + OpUsage otherUsage = rawUsages.get(otherUsageNum); + if (otherUsage.uid == usage.uid) { + if (otherUsage.isRunning && !usage.isRunning) { + isMostRecentForUid = false; + } else if (usage.isRunning + && otherUsage.lastAccessTime >= usage.lastAccessTime) { + isMostRecentForUid = false; + } else if (otherUsage.lastAccessTime >= usage.lastAccessTime) { + isMostRecentForUid = false; + } + + if (!isMostRecentForUid) { + break; + } } } - } - for (int i = 0; i < toRemove.size(); i++) { - rawUsages.remove(toRemove.get(i)); + if (isMostRecentForUid) { + deDuped.add(usage); + } } + + return deDuped; } private boolean isUserSensitive(String packageName, UserHandle user, String op) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 9b570b7cdef9..27ba72b97d7c 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1018,6 +1018,20 @@ public final class Settings { "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS"; /** + * Activity Action: Show settings to manage all SIM profiles. + * <p> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_MANAGE_ALL_SUBSCRIPTIONS_SETTINGS = + "android.settings.MANAGE_ALL_SUBSCRIPTIONS_SETTINGS"; + + /** * Activity Action: Show screen for controlling which apps can draw on top of other apps. * <p> * In some cases, a matching Activity may not exist, so ensure you safeguard against this. diff --git a/core/java/android/service/search/OWNERS b/core/java/android/service/search/OWNERS new file mode 100644 index 000000000000..92835c2b0626 --- /dev/null +++ b/core/java/android/service/search/OWNERS @@ -0,0 +1,2 @@ +hyunyoungs@google.com +sfufa@google.com diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java index 1878d61c78ac..8492363eb503 100644 --- a/core/java/android/text/FontConfig.java +++ b/core/java/android/text/FontConfig.java @@ -19,171 +19,268 @@ package android.text; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.graphics.fonts.FontStyle; import android.graphics.fonts.FontVariationAxis; -import android.net.Uri; import android.os.Build; +import android.os.LocaleList; +import java.io.File; import java.lang.annotation.Retention; +import java.util.List; /** * Font configuration descriptions for System fonts. - * @hide + * @hide // TODO Make this SystemApi. */ public final class FontConfig { - private final @NonNull Family[] mFamilies; - private final @NonNull Alias[] mAliases; + private final @NonNull List<Family> mFamilies; + private final @NonNull List<Alias> mAliases; - public FontConfig(@NonNull Family[] families, @NonNull Alias[] aliases) { + /** + * Construct a SystemFontConfig instance. + * + * @param families a list of font families. + * @param aliases a list of aliases. + * + * @hide Only system server can create this instance and passed via IPC. + */ + public FontConfig(@NonNull List<Family> families, @NonNull List<Alias> aliases) { mFamilies = families; mAliases = aliases; } /** * Returns the ordered list of families included in the system fonts. + * + * @return a list of font families. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public @NonNull Family[] getFamilies() { + public @NonNull List<Family> getFontFamilies() { return mFamilies; } /** * Returns the list of aliases defined for the font families in the system fonts. + * + * @return a list of font families. */ - public @NonNull Alias[] getAliases() { + public @NonNull List<Alias> getAliases() { return mAliases; } /** - * Class that holds information about a Font. + * Returns the ordered list of families included in the system fonts. + * @deprecated Use getFontFamilies instead. + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public @NonNull Family[] getFamilies() { + return mFamilies.toArray(new Family[0]); + } + + /** + * A class represents single font entry in system font configuration. */ public static final class Font { - private final @NonNull String mFontName; - private final int mTtcIndex; - private final @NonNull FontVariationAxis[] mAxes; - private final int mWeight; - private final boolean mIsItalic; - private Uri mUri; - private final String mFallbackFor; + private final @NonNull File mFilePath; + private final @Nullable File mOriginalPath; + private final @NonNull FontStyle mStyle; + private final @IntRange(from = 0) int mIndex; + private final @NonNull String mFontVariationSettings; + private final @Nullable String mFallback; /** - * @hide + * Construct a Font instance. + * + * @hide Only system server can create this instance and passed via IPC. */ - public Font(@NonNull String fontName, int ttcIndex, @NonNull FontVariationAxis[] axes, - int weight, boolean isItalic, String fallbackFor) { - mFontName = fontName; - mTtcIndex = ttcIndex; - mAxes = axes; - mWeight = weight; - mIsItalic = isItalic; - mFallbackFor = fallbackFor; + public Font(@NonNull File filePath, @Nullable File originalPath, @NonNull FontStyle style, + @IntRange(from = 0) int index, @NonNull String fontVariationSettings, + @Nullable String fallback) { + mFilePath = filePath; + mOriginalPath = originalPath; + mStyle = style; + mIndex = index; + mFontVariationSettings = fontVariationSettings; + mFallback = fallback; + } + + /** + * Returns a file to the font file. + * + * @return a font file. + */ + public @NonNull File getFilePath() { + return mFilePath; + } + + /** + * Returns an original font file in the system directory. + * + * If the font file is not updated, returns null. + * + * @return returns the original font file in the system if the font file is updated. Returns + * null if the font file is not updated. + */ + public @Nullable File getOriginalPath() { + return mOriginalPath; + } + + /** + * Returns a font style. + * + * @return a font style. + */ + public @NonNull FontStyle getStyle() { + return mStyle; + } + + /** + * Returns a font index. + * + * @return a font index. + */ + public @IntRange(from = 0) int getIndex() { + return mIndex; } /** - * Returns the name associated by the system to this font. + * Return a font variation settings. + * + * @return a font variation settings. */ - public @NonNull String getFontName() { - return mFontName; + public @NonNull String getFontVariationSettings() { + return mFontVariationSettings; + } + + /** + * Returns font family name that uses this font as a fallback. + * + * If this font is a fallback for the default font family, this is null. + * + * @return a font family name. + */ + public @Nullable String getFallback() { + return mFallback; } /** * Returns the index to be used to access this font when accessing a TTC file. + * @deprecated Use getIndex instead. + * @hide */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getTtcIndex() { - return mTtcIndex; + return mIndex; } /** * Returns the list of axes associated to this font. + * @deprecated Use getFontVariationSettings + * @hide */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @NonNull FontVariationAxis[] getAxes() { - return mAxes; + return FontVariationAxis.fromFontVariationSettings(mFontVariationSettings); } /** * Returns the weight value for this font. + * @deprecated Use getStyle instead. + * @hide */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getWeight() { - return mWeight; + return getStyle().getWeight(); } /** * Returns whether this font is italic. + * @deprecated Use getStyle instead. + * @hide */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean isItalic() { - return mIsItalic; - } - - /** - * Returns the content uri associated to this font. - * - * You can reach to the font contents by calling {@link - * android.content.ContentResolver#openInputStream}. - */ - public @Nullable Uri getUri() { - return mUri; - } - - public void setUri(@NonNull Uri uri) { - mUri = uri; - } - - public String getFallbackFor() { - return mFallbackFor; + return getStyle().getSlant() == FontStyle.FONT_SLANT_ITALIC; } } /** - * Class that holds information about a Font alias. + * A class represents alias between named font families. + * + * In the system font configuration, an font family can be an alias of another font family with + * different font weight. For example, "sans-serif-medium" can be a medium weight of + * sans-serif font family. */ public static final class Alias { - private final @NonNull String mName; - private final @NonNull String mToName; - private final int mWeight; + private final @NonNull String mAliasName; + private final @NonNull String mReferName; + private final @IntRange(from = 0, to = 1000) int mWeight; - public Alias(@NonNull String name, @NonNull String toName, int weight) { - mName = name; - mToName = toName; + /** + * Construct an alias instance. + * + * @param aliasName an alias of the named font family. + * @param referName a referring font family name. + * @param weight a font weight of the referring font family. + * @hide Only system server can create this instance and passed via IPC. + */ + public Alias(@NonNull String aliasName, @NonNull String referName, + @IntRange(from = 0, to = 1000) int weight) { + mAliasName = aliasName; + mReferName = referName; mWeight = weight; } /** - * Returns the new name for the alias. + * An alias of the named font family. + * + * @return an alias of the named font family. */ - public @NonNull String getName() { - return mName; + public @NonNull String getAliasName() { + return mAliasName; } /** - * Returns the existing name to which this alias points to. + * A name of font family referring from {@link #getAliasName()} + * + * @return a referring font family name. */ - public @NonNull String getToName() { - return mToName; + public @NonNull String getReferName() { + return mReferName; } /** - * Returns the weight associated with this alias. + * A font weight of the referring font family. + * + * @return a font weight of the referring font family. */ - public int getWeight() { + public @IntRange(from = 0, to = 1000) int getWeight() { return mWeight; } } /** - * Class that holds information about a Font family. + * A class represents single font family entry in system font configuration. + * + * <p> + * A font family is a bundle of fonts for drawing text in various styles. + * For example, regular style font and bold style font can be bundled into a single font family, + * then system will select the correct style font from family for drawing. */ public static final class Family { - private final @NonNull String mName; - private final @NonNull Font[] mFonts; - // Comma separated BCP47 complient locale strings - private final @NonNull String mLanguages; + private final @NonNull List<Font> mFonts; + private final @Nullable String mName; + private final @Nullable LocaleList mLocaleList; + private final @Variant int mVariant; /** @hide */ @Retention(SOURCE) @@ -212,52 +309,105 @@ public final class FontConfig { /** * Value for font variant. * - * Indiates the font is for elegant variant. + * Indicates the font is for elegant variant. * @see android.graphics.Paint#setElegantTextHeight */ public static final int VARIANT_ELEGANT = 2; - // Must be same with Minikin's variant values. - // See frameworks/minikin/include/minikin/FontFamily.h - private final @Variant int mVariant; - - public Family(@NonNull String name, @NonNull Font[] fonts, @NonNull String languages, - @Variant int variant) { - mName = name; + /** + * Construct a family instance. + * + * @hide Only system server can create this instance and passed via IPC. + */ + public Family(@NonNull List<Font> fonts, @Nullable String name, + @Nullable LocaleList localeList, @Variant int variant) { mFonts = fonts; - mLanguages = languages; + mName = name; + mLocaleList = localeList; mVariant = variant; } /** - * Returns the name given by the system to this font family. + * Returns a list of font files in this family. + * + * @return a list of font files. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public @Nullable String getName() { + public @NonNull List<Font> getFontList() { + return mFonts; + } + + /** + * Returns a family name if this family defines a new fallback. + * + * @return non-null if a family name is associated. Otherwise null. + */ + public @Nullable String getFallbackName() { return mName; } /** - * Returns the list of fonts included in this family. + * Returns a locale list if associated. + * + * @return non-null if a locale list is associated. Otherwise null. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public @Nullable Font[] getFonts() { - return mFonts; + public @NonNull LocaleList getLocaleList() { + return mLocaleList; } /** - * Returns the comma separated BCP47 complient languages for this family. May be null. + * Returns a text height variant. + * + * @return text height variant. */ - public @NonNull String getLanguages() { - return mLanguages; + public @Variant int getTextHeightVariant() { + return mVariant; } /** - * Returns the font variant for this family, e.g. "elegant" or "compact". May be null. + * Returns a family variant associated. + * + * @return a family variant. + * @deprecated Use getTextHeightVariant instead. + * @hide */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @Variant int getVariant() { return mVariant; } + + /** + * Returns a family name if associated. + * + * @return non-null if a family name is associated. Otherwise null. + * @deprecated Use getFallbackName instead. + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public @Nullable String getName() { + return mName; + } + + /** + * Returns the list of fonts included in this family. + * @deprecated Use getFontFiles instead + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public @Nullable Font[] getFonts() { + return mFonts.toArray(new Font[0]); + } + + /** + * Returns the comma separated BCP47 compliant languages for this family. May be null. + * @deprecated Use getLocaleList instead + * @hide + */ + @Deprecated + public @NonNull String getLanguages() { + return mLocaleList.toLanguageTags(); + } } } diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index 17d3ae4a7ff0..471f2c2aecae 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -24,11 +24,12 @@ import android.content.res.Resources; import android.icu.text.MeasureFormat; import android.icu.util.Measure; import android.icu.util.MeasureUnit; -import android.net.NetworkUtils; import android.text.BidiFormatter; import android.text.TextUtils; import android.view.View; +import com.android.net.module.util.Inet4AddressUtils; + import java.util.Locale; /** @@ -207,7 +208,7 @@ public final class Formatter { */ @Deprecated public static String formatIpAddress(int ipv4Address) { - return NetworkUtils.intToInetAddress(ipv4Address).getHostAddress(); + return Inet4AddressUtils.intToInet4AddressHTL(ipv4Address).getHostAddress(); } private static final int SECONDS_PER_MINUTE = 60; diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 2d26c64248cf..b4e1172f49ae 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -642,7 +642,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation updateState(state); applyLocalVisibilityOverride(); - if (!mState.equals(lastState, true /* excludingCaptionInsets */, + if (!mState.equals(lastState, false /* excludingCaptionInsets */, true /* excludeInvisibleIme */)) { if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged"); mHost.notifyInsetsChanged(); @@ -672,16 +672,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation getSourceConsumer(type).updateSource(source, animationType); } for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) { + // Only update the server side insets here. + if (type == ITYPE_CAPTION_BAR) continue; InsetsSource source = mState.peekSource(type); if (source == null) continue; if (newState.peekSource(type) == null) { mState.removeSource(type); } } - if (mCaptionInsetsHeight != 0) { - mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(mFrame.left, mFrame.top, - mFrame.right, mFrame.top + mCaptionInsetsHeight)); - } updateDisabledUserAnimationTypes(disabledUserAnimationTypes); @@ -1488,7 +1486,16 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @Override public void setCaptionInsetsHeight(int height) { - mCaptionInsetsHeight = height; + if (mCaptionInsetsHeight != height) { + mCaptionInsetsHeight = height; + if (mCaptionInsetsHeight != 0) { + mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(mFrame.left, mFrame.top, + mFrame.right, mFrame.top + mCaptionInsetsHeight)); + } else { + mState.removeSource(ITYPE_CAPTION_BAR); + } + mHost.notifyInsetsChanged(); + } } @Override diff --git a/core/java/android/view/InsetsFlags.java b/core/java/android/view/InsetsFlags.java index 5a64a5d5b3a6..a334907c04bc 100644 --- a/core/java/android/view/InsetsFlags.java +++ b/core/java/android/view/InsetsFlags.java @@ -21,7 +21,7 @@ import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE; +import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import android.view.WindowInsetsController.Appearance; @@ -60,13 +60,13 @@ public class InsetsFlags { @ViewDebug.ExportedProperty(flagMapping = { @ViewDebug.FlagToString( - mask = BEHAVIOR_SHOW_BARS_BY_SWIPE, - equals = BEHAVIOR_SHOW_BARS_BY_SWIPE, - name = "SHOW_BARS_BY_SWIPE"), + mask = BEHAVIOR_DEFAULT, + equals = BEHAVIOR_DEFAULT, + name = "DEFAULT"), @ViewDebug.FlagToString( mask = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE, equals = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE, name = "SHOW_TRANSIENT_BARS_BY_SWIPE") }) - public @Behavior int behavior; + public @Behavior int behavior = BEHAVIOR_DEFAULT; } diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index bf377b0bcfd7..d68e9032c19d 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -106,7 +106,9 @@ public class InsetsState implements Parcelable { public static final int ITYPE_NAVIGATION_BAR = 1; public static final int ITYPE_CAPTION_BAR = 2; - public static final int ITYPE_TOP_GESTURES = 3; + // The always visible types are visible to all windows regardless of the z-order. + public static final int FIRST_ALWAYS_VISIBLE_TYPE = 3; + public static final int ITYPE_TOP_GESTURES = FIRST_ALWAYS_VISIBLE_TYPE; public static final int ITYPE_BOTTOM_GESTURES = 4; public static final int ITYPE_LEFT_GESTURES = 5; public static final int ITYPE_RIGHT_GESTURES = 6; @@ -117,15 +119,16 @@ public class InsetsState implements Parcelable { public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9; public static final int ITYPE_RIGHT_MANDATORY_GESTURES = 10; - public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 11; - public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 12; - public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = 13; - public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 14; + public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 11; + public static final int ITYPE_TOP_DISPLAY_CUTOUT = 12; + public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 13; + public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 14; + public static final int LAST_ALWAYS_VISIBLE_TYPE = ITYPE_BOTTOM_DISPLAY_CUTOUT; - public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 15; - public static final int ITYPE_TOP_DISPLAY_CUTOUT = 16; - public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 17; - public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 18; + public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 15; + public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 16; + public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = 17; + public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 18; /** Input method window. */ public static final int ITYPE_IME = 19; @@ -182,6 +185,18 @@ public class InsetsState implements Parcelable { } /** + * Mirror the always visible sources from the other state. They will share the same object for + * the always visible types. + * + * @param other the state to mirror the mirrored sources from. + */ + public void mirrorAlwaysVisibleInsetsSources(InsetsState other) { + for (int type = FIRST_ALWAYS_VISIBLE_TYPE; type <= LAST_ALWAYS_VISIBLE_TYPE; type++) { + mSources[type] = other.mSources[type]; + } + } + + /** * Calculates {@link WindowInsets} based on the current source configuration. * * @param frame The frame to calculate the insets relative to. diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java index c018d1cf1782..c61baf6fb40c 100644 --- a/core/java/android/view/PendingInsetsController.java +++ b/core/java/android/view/PendingInsetsController.java @@ -100,6 +100,9 @@ public class PendingInsetsController implements WindowInsetsController { if (mReplayedInsetsController != null) { return mReplayedInsetsController.getSystemBarsBehavior(); } + if (mBehavior == KEEP_BEHAVIOR) { + return BEHAVIOR_DEFAULT; + } return mBehavior; } diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java index ac5d14e97c85..258a72cbcab4 100644 --- a/core/java/android/view/RemoteAnimationTarget.java +++ b/core/java/android/view/RemoteAnimationTarget.java @@ -270,7 +270,7 @@ public class RemoteAnimationTarget implements Parcelable { pw.print(" clipRect="); clipRect.printShortString(pw); pw.print(" contentInsets="); contentInsets.printShortString(pw); pw.print(" prefixOrderIndex="); pw.print(prefixOrderIndex); - pw.print(" position="); position.dump(pw); + pw.print(" position="); printPoint(position, pw); pw.print(" sourceContainerBounds="); sourceContainerBounds.printShortString(pw); pw.print(" screenSpaceBounds="); screenSpaceBounds.printShortString(pw); pw.print(" localBounds="); localBounds.printShortString(pw); @@ -303,6 +303,10 @@ public class RemoteAnimationTarget implements Parcelable { proto.end(token); } + private static void printPoint(Point p, PrintWriter pw) { + pw.print("["); pw.print(p.x); pw.print(","); pw.print(p.y); pw.print("]"); + } + public static final @android.annotation.NonNull Creator<RemoteAnimationTarget> CREATOR = new Creator<RemoteAnimationTarget>() { public RemoteAnimationTarget createFromParcel(Parcel in) { diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 26e3bb27a788..7ac57b5e0421 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -232,7 +232,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private final Matrix mTmpMatrix = new Matrix(); SurfaceControlViewHost.SurfacePackage mSurfacePackage; - private final boolean mUseBlastSync = false; + private final boolean mUseBlastSync = true; /** * Returns {@code true} if buffers should be submitted via blast diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 25967b3ebcff..b8840ba9e43a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3790,7 +3790,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <p>Since this flag is a modifier for {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, it only * has an effect when used in combination with that flag.</p> * - * @deprecated Use {@link WindowInsetsController#BEHAVIOR_SHOW_BARS_BY_SWIPE} instead. + * @deprecated Use {@link WindowInsetsController#BEHAVIOR_DEFAULT} instead. */ @Deprecated public static final int SYSTEM_UI_FLAG_IMMERSIVE = 0x00000800; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7e0ebbcc61d2..426edbceaa45 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -26,7 +26,6 @@ import static android.view.InsetsState.SIZE; import static android.view.View.PFLAG_DRAW_ANIMATION; import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; -import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE; import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; @@ -55,8 +54,7 @@ import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH; +import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; @@ -2167,10 +2165,8 @@ public final class ViewRootImpl implements ViewParent, if ((sysUiVis & SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0 || (flags & FLAG_FULLSCREEN) != 0) { inOutParams.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; - } else if ((sysUiVis & SYSTEM_UI_FLAG_IMMERSIVE) != 0) { - inOutParams.insetsFlags.behavior = BEHAVIOR_SHOW_BARS_BY_SWIPE; } else { - inOutParams.insetsFlags.behavior = BEHAVIOR_SHOW_BARS_BY_TOUCH; + inOutParams.insetsFlags.behavior = BEHAVIOR_DEFAULT; } } @@ -9154,14 +9150,14 @@ public final class ViewRootImpl implements ViewParent, * Handles an inbound request for scroll capture from the system. If a client is not already * active, a search will be dispatched through the view tree to locate scrolling content. * <p> - * Either {@link IScrollCaptureCallbacks#onClientConnected(IScrollCaptureConnection, Rect, + * Either {@link IScrollCaptureCallbacks#onConnected(IScrollCaptureConnection, Rect, * Point)} or {@link IScrollCaptureCallbacks#onUnavailable()} will be returned * depending on the results of the search. * * @param callbacks to receive responses * @see ScrollCaptureTargetResolver */ - private void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) { + public void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) { LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>(); // Window (root) level callbacks @@ -9169,10 +9165,12 @@ public final class ViewRootImpl implements ViewParent, // Search through View-tree View rootView = getView(); - Point point = new Point(); - Rect rect = new Rect(0, 0, rootView.getWidth(), rootView.getHeight()); - getChildVisibleRect(rootView, rect, point); - rootView.dispatchScrollCaptureSearch(rect, point, targetList); + if (rootView != null) { + Point point = new Point(); + Rect rect = new Rect(0, 0, rootView.getWidth(), rootView.getHeight()); + getChildVisibleRect(rootView, rect, point); + rootView.dispatchScrollCaptureSearch(rect, point, targetList); + } // No-op path. Scroll capture not offered for this window. if (targetList.isEmpty()) { diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java index e879bb4bff95..fb9bcbd2fcb2 100644 --- a/core/java/android/view/WindowInsetsController.java +++ b/core/java/android/view/WindowInsetsController.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Insets; import android.inputmethodservice.InputMethodService; +import android.os.Build; import android.os.CancellationSignal; import android.view.InsetsState.InternalInsetsType; import android.view.WindowInsets.Type; @@ -77,22 +78,41 @@ public interface WindowInsetsController { } /** - * The default option for {@link #setSystemBarsBehavior(int)}. System bars will be forcibly - * shown on any user interaction on the corresponding display if navigation bars are hidden by + * Option for {@link #setSystemBarsBehavior(int)}. System bars will be forcibly shown on any + * user interaction on the corresponding display if navigation bars are hidden by * {@link #hide(int)} or * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}. + * @deprecated This is not supported on Android {@link Build.VERSION_CODES#S} and later. Use + * {@link #BEHAVIOR_DEFAULT} or {@link #BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE} + * instead. */ + @Deprecated int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0; /** + * The default option for {@link #setSystemBarsBehavior(int)}: Window would like to remain + * interactive when hiding navigation bars by calling {@link #hide(int)} or + * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}. + * + * <p>When system bars are hidden in this mode, they can be revealed with system gestures, such + * as swiping from the edge of the screen where the bar is hidden from.</p> + * + * <p>When the gesture navigation is enabled, the system gestures can be triggered regardless + * the visibility of system bars.</p> + */ + int BEHAVIOR_DEFAULT = 1; + + /** * Option for {@link #setSystemBarsBehavior(int)}: Window would like to remain interactive when * hiding navigation bars by calling {@link #hide(int)} or * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}. * * <p>When system bars are hidden in this mode, they can be revealed with system gestures, such * as swiping from the edge of the screen where the bar is hidden from.</p> + * @deprecated Use {@link #BEHAVIOR_DEFAULT} instead. */ - int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1; + @Deprecated + int BEHAVIOR_SHOW_BARS_BY_SWIPE = BEHAVIOR_DEFAULT; /** * Option for {@link #setSystemBarsBehavior(int)}: Window would like to remain interactive when @@ -111,8 +131,7 @@ public interface WindowInsetsController { * @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(value = {BEHAVIOR_SHOW_BARS_BY_TOUCH, BEHAVIOR_SHOW_BARS_BY_SWIPE, - BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE}) + @IntDef(value = {BEHAVIOR_DEFAULT, BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE}) @interface Behavior { } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 4f7e841bd072..45fa41b252aa 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -663,24 +663,34 @@ public interface WindowManager extends ViewManager { } /** - * Message for taking fullscreen screenshot + * Invoke screenshot flow to capture a full-screen image. * @hide */ int TAKE_SCREENSHOT_FULLSCREEN = 1; /** - * Message for taking screenshot of selected region. + * Invoke screenshot flow allowing the user to select a region. * @hide */ int TAKE_SCREENSHOT_SELECTED_REGION = 2; /** - * Message for handling a screenshot flow with an image provided by the caller. + * Invoke screenshot flow with an image provided by the caller. * @hide */ int TAKE_SCREENSHOT_PROVIDED_IMAGE = 3; /** + * Enum listing the types of screenshot requests available. + * + * @hide + */ + @IntDef({TAKE_SCREENSHOT_FULLSCREEN, + TAKE_SCREENSHOT_SELECTED_REGION, + TAKE_SCREENSHOT_PROVIDED_IMAGE}) + @interface ScreenshotType {} + + /** * Enum listing the possible sources from which a screenshot was originated. Used for logging. * * @hide diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java index 8c355202b63f..dd55f049b364 100644 --- a/core/java/android/view/WindowManagerPolicyConstants.java +++ b/core/java/android/view/WindowManagerPolicyConstants.java @@ -89,6 +89,15 @@ public interface WindowManagerPolicyConstants { */ String EXTRA_FROM_HOME_KEY = "android.intent.extra.FROM_HOME_KEY"; + /** + * Extra for the start reason of the HOME intent. + * Will be {@link PowerManager#WAKE_REASON_WAKE_KEY} or + * {@link PowerManager#WAKE_REASON_POWER_BUTTON} when intent was sent through + * {@link PhoneWindowManager#shouldWakeUpWithHomeIntent}. + * @hide + */ + String EXTRA_START_REASON = "android.intent.extra.EXTRA_START_REASON"; + // TODO: move this to a more appropriate place. interface PointerEventListener { /** diff --git a/core/java/android/view/accessibility/OWNERS b/core/java/android/view/accessibility/OWNERS index 93b5a2e8bc28..b1d3967a8b04 100644 --- a/core/java/android/view/accessibility/OWNERS +++ b/core/java/android/view/accessibility/OWNERS @@ -9,3 +9,4 @@ sumir@google.com ogunwale@google.com jjaggi@google.com pweaver@google.com +ryanlwlin@google.com diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java index 5b32649ac55b..c1913f69546c 100644 --- a/core/java/android/view/textclassifier/TextSelection.java +++ b/core/java/android/view/textclassifier/TextSelection.java @@ -137,6 +137,15 @@ public final class TextSelection implements Parcelable { return mExtras; } + /** @hide */ + public TextSelection.Builder toBuilder() { + return new TextSelection.Builder(mStartIndex, mEndIndex) + .setId(mId) + .setEntityConfidence(mEntityConfidence) + .setTextClassification(mTextClassification) + .setExtras(mExtras); + } + @Override public String toString() { return String.format( @@ -188,6 +197,12 @@ public final class TextSelection implements Parcelable { return this; } + Builder setEntityConfidence(EntityConfidence scores) { + mEntityConfidence.clear(); + mEntityConfidence.putAll(scores.toMap()); + return this; + } + /** * Sets an id for the TextSelection object. */ diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java index d34c8d58e789..578ed8c948e2 100644 --- a/core/java/android/view/textservice/TextServicesManager.java +++ b/core/java/android/view/textservice/TextServicesManager.java @@ -17,8 +17,8 @@ package android.view.textservice; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemService; -import android.annotation.TestApi; import android.annotation.UserIdInt; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -34,6 +34,8 @@ import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener; import com.android.internal.textservice.ISpellCheckerSessionListener; import com.android.internal.textservice.ITextServicesManager; +import java.util.Arrays; +import java.util.List; import java.util.Locale; /** @@ -232,9 +234,22 @@ public final class TextServicesManager { } /** - * @hide + * Retrieve the list of currently enabled spell checkers, or null if there is none. + * + * @return The list of currently enabled spell checkers. */ - @UnsupportedAppUsage + @Nullable + public List<SpellCheckerInfo> getEnabledSpellCheckersList() { + final SpellCheckerInfo[] enabledSpellCheckers = getEnabledSpellCheckers(); + return enabledSpellCheckers != null ? Arrays.asList(enabledSpellCheckers) : null; + } + + /** + * Retrieve the currently active spell checker, or null if there is none. + * + * @return The current active spell checker info. + */ + @Nullable public SpellCheckerInfo getCurrentSpellChecker() { try { // Passing null as a locale for ICS @@ -245,9 +260,13 @@ public final class TextServicesManager { } /** - * @hide + * Retrieve the selected subtype of the selected spell checker, or null if there is none. + * + * @param allowImplicitlySelectedSubtype {@code true} to return the default language matching + * system locale if there's no subtype selected explicitly, otherwise, returns null. + * @return The meta information of the selected subtype of the selected spell checker. */ - @UnsupportedAppUsage + @Nullable public SpellCheckerSubtype getCurrentSpellCheckerSubtype( boolean allowImplicitlySelectedSubtype) { try { @@ -258,10 +277,10 @@ public final class TextServicesManager { } /** - * @hide + * Return whether the spell checker is enabled or not. + * + * @return {@code true} if spell checker is enabled, {@code false} otherwise. */ - @UnsupportedAppUsage - @TestApi public boolean isSpellCheckerEnabled() { try { return mService.isSpellCheckerEnabled(mUserId); diff --git a/core/java/android/view/translation/ITranslationManager.aidl b/core/java/android/view/translation/ITranslationManager.aidl index 73addf4d1894..e1754531d761 100644 --- a/core/java/android/view/translation/ITranslationManager.aidl +++ b/core/java/android/view/translation/ITranslationManager.aidl @@ -16,10 +16,15 @@ package android.view.translation; +import android.content.ComponentName; +import android.os.IBinder; import android.service.translation.TranslationRequest; +import android.view.autofill.AutofillId; import android.view.translation.TranslationSpec; import com.android.internal.os.IResultReceiver; +import java.util.List; + /** * Mediator between apps being translated and translation service implementation. * @@ -29,4 +34,8 @@ oneway interface ITranslationManager { void getSupportedLocales(in IResultReceiver receiver, int userId); void onSessionCreated(in TranslationSpec sourceSpec, in TranslationSpec destSpec, int sessionId, in IResultReceiver receiver, int userId); + + void updateUiTranslationState(int state, in TranslationSpec sourceSpec, + in TranslationSpec destSpec, in List<AutofillId> viewIds, in int taskId, + int userId); } diff --git a/core/java/android/view/translation/UiTranslationManager.java b/core/java/android/view/translation/UiTranslationManager.java new file mode 100644 index 000000000000..eeb463ae0ed3 --- /dev/null +++ b/core/java/android/view/translation/UiTranslationManager.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.translation; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.content.Context; +import android.os.RemoteException; +import android.view.View; +import android.view.autofill.AutofillId; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.Objects; + +/** + * The {@link UiTranslationManager} class provides ways for apps to use the ui translation + * function in framework. + * + * @hide + */ +@SystemApi +public final class UiTranslationManager { + + private static final String TAG = "UiTranslationManager"; + + /** + * The state caller request to disable utranslation,, it is no longer need to ui translation. + * + * @hide + */ + public static final int STATE_UI_TRANSLATION_STARTED = 0; + /** + * The state caller request to pause ui translation, it will switch back to the original text. + * + * @hide + */ + public static final int STATE_UI_TRANSLATION_PAUSED = 1; + /** + * The state caller request to resume the paused ui translation, it will show the translated + * text again if the text had been translated. + * + * @hide + */ + public static final int STATE_UI_TRANSLATION_RESUMED = 2; + /** + * The state the caller request to enable ui translation. + * + * @hide + */ + public static final int STATE_UI_TRANSLATION_FINISHED = 3; + /** + * @hide + */ + @IntDef(prefix = {"STATE__TRANSLATION"}, value = { + STATE_UI_TRANSLATION_STARTED, + STATE_UI_TRANSLATION_PAUSED, + STATE_UI_TRANSLATION_RESUMED, + STATE_UI_TRANSLATION_FINISHED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface UiTranslationState { + } + + @NonNull + private final Context mContext; + + private final ITranslationManager mService; + + /** + * @hide + */ + public UiTranslationManager(@NonNull Context context, ITranslationManager service) { + mContext = Objects.requireNonNull(context); + mService = service; + } + + /** + * Request ui translation for a given Views. + * + * @param sourceSpec {@link TranslationSpec} for the data to be translated. + * @param destSpec {@link TranslationSpec} for the translated data. + * @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be translated + * @param taskId the Activity Task id which needs ui translation + */ + @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) + public void startTranslation(@NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds, + int taskId) { + // TODO(b/177789967): Return result code or find a way to notify the status. + // TODO(b/177394471): The is a temparary API, the expected is requestUiTranslation( + // TranslationSpec, TranslationSpec,List<AutofillId>, Binder). We may need more time to + // implement it, use task id as initial version for demo. + Objects.requireNonNull(sourceSpec); + Objects.requireNonNull(destSpec); + Objects.requireNonNull(viewIds); + + try { + mService.updateUiTranslationState(STATE_UI_TRANSLATION_STARTED, sourceSpec, + destSpec, viewIds, taskId, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Request to disable the ui translation. It will destroy all the {@link Translator}s and no + * longer to show to show the translated text. + * + * @param taskId the Activity Task id which needs ui translation + */ + @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) + public void finishTranslation(int taskId) { + try { + // TODO(b/177394471): The is a temparary API, the expected is finishUiTranslation( + // Binder). We may need more time to implement it, use task id as initial version. + mService.updateUiTranslationState(STATE_UI_TRANSLATION_FINISHED, + null /* sourceSpec */, null /* destSpec*/, null /* viewIds */, taskId, + mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Request to pause the current ui translation's {@link Translator} which will switch back to + * the original language. + * + * @param taskId the Activity Task id which needs ui translation + */ + @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) + public void pauseTranslation(int taskId) { + try { + // TODO(b/177394471): The is a temparary API, the expected is pauseUiTranslation(Binder) + // We may need more time to implement it, use task id as initial version for demo + mService.updateUiTranslationState(STATE_UI_TRANSLATION_PAUSED, + null /* sourceSpec */, null /* destSpec*/, null /* viewIds */, taskId, + mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Request to resume the paused ui translation's {@link Translator} which will switch to the + * translated language if the text had been translated. + * + * @param taskId the Activity Task id which needs ui translation + */ + @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) + public void resumeTranslation(int taskId) { + try { + // TODO(b/177394471): The is a temparary API, the expected is resumeUiTranslation( + // Binder). We may need more time to implement it, use task id as initial version. + mService.updateUiTranslationState(STATE_UI_TRANSLATION_RESUMED, + null /* sourceSpec */, null /* destSpec*/, null /* viewIds */, + taskId, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } +} diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index b9ff26b7d9ff..6281ee9d05d1 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -39,6 +39,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.inspector.InspectableProperty; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import java.util.ArrayList; @@ -776,17 +777,23 @@ public abstract class AbsSeekBar extends ProgressBar { /** * Grows {@code r} from its center such that each dimension is at least {@code minimumSize}. + * + * The result will still have the same {@link Rect#centerX()} and {@link Rect#centerY()} as the + * input. + * + * @hide */ - private void growRectTo(Rect r, int minimumSize) { - int dy = (minimumSize - r.height()) / 2; + @VisibleForTesting + public void growRectTo(Rect r, int minimumSize) { + int dy = minimumSize - r.height(); if (dy > 0) { - r.top -= dy; - r.bottom += dy; + r.top -= (dy + 1) / 2; + r.bottom += dy / 2; } - int dx = (minimumSize - r.width()) / 2; + int dx = minimumSize - r.width(); if (dx > 0) { - r.left -= dx; - r.right += dx; + r.left -= (dx + 1) / 2; + r.right += dx / 2; } } diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index e4de4001c3e2..ed20d26a8b47 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -196,11 +196,6 @@ public class ImageView extends View { initImageView(); - // ImageView is not important by default, unless app developer overrode attribute. - if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) { - setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_NO); - } - final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.ImageView, defStyleAttr, defStyleRes); saveAttributeDataForStyleable(context, R.styleable.ImageView, @@ -265,6 +260,15 @@ public class ImageView extends View { sCompatDrawableVisibilityDispatch = targetSdkVersion < Build.VERSION_CODES.N; sCompatDone = true; } + + // By default, ImageView is not important for autofill but important for content capture. + // Developers can override these defaults via the corresponding attributes. + if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) { + setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_NO); + } + if (getImportantForContentCapture() == IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) { + setImportantForContentCapture(IMPORTANT_FOR_CONTENT_CAPTURE_YES); + } } @Override diff --git a/core/java/android/window/IRemoteTransition.aidl b/core/java/android/window/IRemoteTransition.aidl new file mode 100644 index 000000000000..e0ddf056653f --- /dev/null +++ b/core/java/android/window/IRemoteTransition.aidl @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import android.view.IRemoteAnimationFinishedCallback; +import android.view.SurfaceControl; +import android.window.TransitionInfo; + +/** + * Interface allowing remote processes to play transition animations. + * The usage flow is as follows: + * <p><ol> + * <li>The remote tags a lifecycle event with an IRemoteTransition (via a parameter in + * ActivityOptions#makeRemoteAnimation) or a transition matches a filter registered via + * Transitions#registerRemote. + * <li>Shell then associates the transition for the event with the IRemoteTransition + * <li>Shell receives onTransitionReady and delegates the animation to the IRemoteTransition + * via {@link #startAnimation}. + * <li>Once the IRemoteTransition is done animating, it will call the finishCallback. + * <li>Shell/Core finish-up the transition. + * </ul> + * + * {@hide} + */ +oneway interface IRemoteTransition { + /** + * Starts a transition animation. Once complete, the implementation should call + * `finishCallback`. + */ + void startAnimation(in TransitionInfo info, in SurfaceControl.Transaction t, + in IRemoteAnimationFinishedCallback finishCallback); +} diff --git a/core/java/android/window/ITransitionPlayer.aidl b/core/java/android/window/ITransitionPlayer.aidl index 55d47cb7ed8b..af37fbc6310f 100644 --- a/core/java/android/window/ITransitionPlayer.aidl +++ b/core/java/android/window/ITransitionPlayer.aidl @@ -16,10 +16,9 @@ package android.window; -import android.app.ActivityManager; import android.view.SurfaceControl; import android.window.TransitionInfo; -import android.window.WindowContainerTransaction; +import android.window.TransitionRequestInfo; /** * Implemented by WMShell to initiate and play transition animations. @@ -56,12 +55,9 @@ oneway interface ITransitionPlayer { * Called when something in WMCore requires a transition to play -- for example when an Activity * is started in a new Task. * - * @param type The {@link WindowManager#TransitionType} of the transition to start. * @param transitionToken An identifying token for the transition that needs to be started. * Pass this to {@link IWindowOrganizerController#startTransition}. - * @param triggerTask If non-null, the task containing the activity whose lifecycle change - * (start or finish) has caused this transition to occur. + * @param request Information about this particular request. */ - void requestStartTransition(int type, in IBinder transitionToken, - in ActivityManager.RunningTaskInfo triggerTask); + void requestStartTransition(in IBinder transitionToken, in TransitionRequestInfo request); } diff --git a/core/java/android/window/TransitionFilter.aidl b/core/java/android/window/TransitionFilter.aidl new file mode 100644 index 000000000000..19c76d153be0 --- /dev/null +++ b/core/java/android/window/TransitionFilter.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +parcelable TransitionFilter; diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java new file mode 100644 index 000000000000..4421f06460a0 --- /dev/null +++ b/core/java/android/window/TransitionFilter.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.WindowConfiguration; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A parcelable filter that can be used for rerouting transitions to a remote. This is a local + * representation so that the transition system doesn't need to make blocking queries over + * binder. + * + * @hide + */ +public final class TransitionFilter implements Parcelable { + + /** + * When non-null: this is a list of transition types that this filter applies to. This filter + * will fail for transitions that aren't one of these types. + */ + @Nullable public int[] mTypeSet = null; + + /** + * A list of required changes. To pass, a transition must meet all requirements. + */ + @Nullable public Requirement[] mRequirements = null; + + public TransitionFilter() { + } + + private TransitionFilter(Parcel in) { + mTypeSet = in.createIntArray(); + mRequirements = in.createTypedArray(Requirement.CREATOR); + } + + /** @return true if `info` meets all the requirements to pass this filter. */ + public boolean matches(@NonNull TransitionInfo info) { + if (mTypeSet != null) { + // non-null typeset, so make sure info is one of the types. + boolean typePass = false; + for (int i = 0; i < mTypeSet.length; ++i) { + if (info.getType() == mTypeSet[i]) { + typePass = true; + break; + } + } + if (!typePass) return false; + } + // Make sure info meets all of the requirements. + if (mRequirements != null) { + for (int i = 0; i < mRequirements.length; ++i) { + if (!mRequirements[i].matches(info)) return false; + } + } + return true; + } + + @Override + /** @hide */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeIntArray(mTypeSet); + dest.writeTypedArray(mRequirements, flags); + } + + @NonNull + public static final Creator<TransitionFilter> CREATOR = + new Creator<TransitionFilter>() { + @Override + public TransitionFilter createFromParcel(Parcel in) { + return new TransitionFilter(in); + } + + @Override + public TransitionFilter[] newArray(int size) { + return new TransitionFilter[size]; + } + }; + + @Override + /** @hide */ + public int describeContents() { + return 0; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("{types=["); + if (mTypeSet != null) { + for (int i = 0; i < mTypeSet.length; ++i) { + sb.append((i == 0 ? "" : ",") + mTypeSet[i]); + } + } + sb.append("] checks=["); + if (mRequirements != null) { + for (int i = 0; i < mRequirements.length; ++i) { + sb.append((i == 0 ? "" : ",") + mRequirements[i]); + } + } + return sb.append("]}").toString(); + } + + /** + * Matches a change that a transition must contain to pass this filter. All requirements in a + * filter must be met to pass the filter. + */ + public static final class Requirement implements Parcelable { + public int mActivityType = ACTIVITY_TYPE_UNDEFINED; + public int[] mModes = null; + + public Requirement() { + } + + private Requirement(Parcel in) { + mActivityType = in.readInt(); + mModes = in.createIntArray(); + } + + /** Go through changes and find if at-least one change matches this filter */ + boolean matches(@NonNull TransitionInfo info) { + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + if (change.getParent() != null) { + // Only look at the top animating windows. + continue; + } + if (mActivityType != ACTIVITY_TYPE_UNDEFINED) { + if (change.getTaskInfo() == null + || change.getTaskInfo().getActivityType() != mActivityType) { + continue; + } + } + if (mModes != null) { + boolean pass = false; + for (int m = 0; m < mModes.length; ++m) { + if (mModes[m] == change.getMode()) { + pass = true; + break; + } + } + if (!pass) continue; + } + return true; + } + return false; + } + + /** Check if the request matches this filter. It may generate false positives */ + boolean matches(@NonNull TransitionRequestInfo request) { + // Can't check modes since the transition hasn't been built at this point. + if (mActivityType == ACTIVITY_TYPE_UNDEFINED) return true; + return request.getTriggerTask() != null + && request.getTriggerTask().getActivityType() == mActivityType; + } + + @Override + /** @hide */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mActivityType); + dest.writeIntArray(mModes); + } + + @NonNull + public static final Creator<Requirement> CREATOR = + new Creator<Requirement>() { + @Override + public Requirement createFromParcel(Parcel in) { + return new Requirement(in); + } + + @Override + public Requirement[] newArray(int size) { + return new Requirement[size]; + } + }; + + @Override + /** @hide */ + public int describeContents() { + return 0; + } + + @Override + public String toString() { + StringBuilder out = new StringBuilder(); + out.append("{atype=" + WindowConfiguration.activityTypeToString(mActivityType)); + out.append(" modes=["); + if (mModes != null) { + for (int i = 0; i < mModes.length; ++i) { + out.append((i == 0 ? "" : ",") + TransitionInfo.modeToString(mModes[i])); + } + } + return out.append("]}").toString(); + } + } +} diff --git a/core/java/android/window/TransitionRequestInfo.aidl b/core/java/android/window/TransitionRequestInfo.aidl new file mode 100644 index 000000000000..d2b9ccfd657e --- /dev/null +++ b/core/java/android/window/TransitionRequestInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +parcelable TransitionRequestInfo; diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java new file mode 100644 index 000000000000..cc493ab63e22 --- /dev/null +++ b/core/java/android/window/TransitionRequestInfo.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.os.Parcelable; +import android.view.WindowManager; + +import com.android.internal.util.DataClass; + +/** + * Used to communicate information about what is changing during a transition to a TransitionPlayer. + * @hide + */ +@DataClass(genToString = true, genSetters = true, genAidl = true) +public final class TransitionRequestInfo implements Parcelable { + + /** The type of the transition being requested. */ + private final @WindowManager.TransitionType int mType; + + /** + * If non-null, If non-null, the task containing the activity whose lifecycle change (start or + * finish) has caused this transition to occur. + */ + private @Nullable ActivityManager.RunningTaskInfo mTriggerTask; + + /** If non-null, a remote-transition associated with the source of this transition. */ + private @Nullable IRemoteTransition mRemoteTransition; + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/window/TransitionRequestInfo.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new TransitionRequestInfo. + * + * @param type + * The type of the transition being requested. + * @param triggerTask + * If non-null, If non-null, the task containing the activity whose lifecycle change (start or + * finish) has caused this transition to occur. + * @param remoteTransition + * If non-null, a remote-transition associated with the source of this transition. + */ + @DataClass.Generated.Member + public TransitionRequestInfo( + @WindowManager.TransitionType int type, + @Nullable ActivityManager.RunningTaskInfo triggerTask, + @Nullable IRemoteTransition remoteTransition) { + this.mType = type; + com.android.internal.util.AnnotationValidations.validate( + WindowManager.TransitionType.class, null, mType); + this.mTriggerTask = triggerTask; + this.mRemoteTransition = remoteTransition; + + // onConstructed(); // You can define this method to get a callback + } + + /** + * The type of the transition being requested. + */ + @DataClass.Generated.Member + public @WindowManager.TransitionType int getType() { + return mType; + } + + /** + * If non-null, If non-null, the task containing the activity whose lifecycle change (start or + * finish) has caused this transition to occur. + */ + @DataClass.Generated.Member + public @Nullable ActivityManager.RunningTaskInfo getTriggerTask() { + return mTriggerTask; + } + + /** + * If non-null, a remote-transition associated with the source of this transition. + */ + @DataClass.Generated.Member + public @Nullable IRemoteTransition getRemoteTransition() { + return mRemoteTransition; + } + + /** + * If non-null, If non-null, the task containing the activity whose lifecycle change (start or + * finish) has caused this transition to occur. + */ + @DataClass.Generated.Member + public @android.annotation.NonNull TransitionRequestInfo setTriggerTask(@android.annotation.NonNull ActivityManager.RunningTaskInfo value) { + mTriggerTask = value; + return this; + } + + /** + * If non-null, a remote-transition associated with the source of this transition. + */ + @DataClass.Generated.Member + public @android.annotation.NonNull TransitionRequestInfo setRemoteTransition(@android.annotation.NonNull IRemoteTransition value) { + mRemoteTransition = value; + return this; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "TransitionRequestInfo { " + + "type = " + mType + ", " + + "triggerTask = " + mTriggerTask + ", " + + "remoteTransition = " + mRemoteTransition + + " }"; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@android.annotation.NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mTriggerTask != null) flg |= 0x2; + if (mRemoteTransition != null) flg |= 0x4; + dest.writeByte(flg); + dest.writeInt(mType); + if (mTriggerTask != null) dest.writeTypedObject(mTriggerTask, flags); + if (mRemoteTransition != null) dest.writeStrongInterface(mRemoteTransition); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ TransitionRequestInfo(@android.annotation.NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + int type = in.readInt(); + ActivityManager.RunningTaskInfo triggerTask = (flg & 0x2) == 0 ? null : (ActivityManager.RunningTaskInfo) in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR); + IRemoteTransition remoteTransition = (flg & 0x4) == 0 ? null : IRemoteTransition.Stub.asInterface(in.readStrongBinder()); + + this.mType = type; + com.android.internal.util.AnnotationValidations.validate( + WindowManager.TransitionType.class, null, mType); + this.mTriggerTask = triggerTask; + this.mRemoteTransition = remoteTransition; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @android.annotation.NonNull Parcelable.Creator<TransitionRequestInfo> CREATOR + = new Parcelable.Creator<TransitionRequestInfo>() { + @Override + public TransitionRequestInfo[] newArray(int size) { + return new TransitionRequestInfo[size]; + } + + @Override + public TransitionRequestInfo createFromParcel(@android.annotation.NonNull android.os.Parcel in) { + return new TransitionRequestInfo(in); + } + }; + + @DataClass.Generated( + time = 1610060387917L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java", + inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.window.IRemoteTransition mRemoteTransition\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index c235c82de720..6cfd49888fac 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -182,7 +182,7 @@ public class ChooserActivity extends ResolverActivity implements * To be used for shared element transition into this activity. * @hide */ - public static final String FIRST_IMAGE_PREVIEW_TRANSITION_NAME = "chooser_preview_image_1"; + public static final String FIRST_IMAGE_PREVIEW_TRANSITION_NAME = "screenshot_preview_image"; private static final String PREF_NUM_SHEET_EXPANSIONS = "pref_num_sheet_expansions"; diff --git a/core/java/com/android/internal/app/ChooserActivityLogger.java b/core/java/com/android/internal/app/ChooserActivityLogger.java index c0cc483648fa..47d83346d038 100644 --- a/core/java/com/android/internal/app/ChooserActivityLogger.java +++ b/core/java/com/android/internal/app/ChooserActivityLogger.java @@ -120,7 +120,7 @@ public interface ChooserActivityLogger { @UiEvent(doc = "User selected the nearby target.") SHARESHEET_NEARBY_TARGET_SELECTED(626), @UiEvent(doc = "User selected the edit target.") - SHARESHEET_EDIT_TARGET_SELECTED(627); + SHARESHEET_EDIT_TARGET_SELECTED(669); private final int mId; SharesheetTargetSelectedEvent(int id) { diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java index c0648ab89c41..2e7629a76dee 100644 --- a/core/java/com/android/internal/net/VpnConfig.java +++ b/core/java/com/android/internal/net/VpnConfig.java @@ -129,7 +129,7 @@ public class VpnConfig implements Parcelable { String[] routes = routesStr.trim().split(" "); for (String route : routes) { //each route is ip/prefix - RouteInfo info = new RouteInfo(new IpPrefix(route), null); + RouteInfo info = new RouteInfo(new IpPrefix(route), null, null, RouteInfo.RTN_UNICAST); this.routes.add(info); updateAllowedFamilies(info.getDestination().getAddress()); } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 33aa19078c9f..f105320d0353 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -19,6 +19,8 @@ package com.android.internal.os; import static android.os.BatteryStatsManager.NUM_WIFI_STATES; import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES; +import static com.android.internal.power.MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -103,7 +105,6 @@ import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeRead import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader; -import com.android.internal.power.MeasuredEnergyArray; import com.android.internal.power.MeasuredEnergyStats; import com.android.internal.power.MeasuredEnergyStats.EnergyBucket; import com.android.internal.util.ArrayUtils; @@ -370,14 +371,6 @@ public class BatteryStatsImpl extends BatteryStats { * @param railStats */ void fillRailDataStats(RailStats railStats); - /** - * Function to get energy consumption data - * - * @return an array of measured energy (in microjoules) since boot, will be null if - * measured energy data is unavailable - */ - @Nullable - MeasuredEnergyArray getEnergyConsumptionData(); } public static abstract class UserInfoProvider { @@ -10704,15 +10697,13 @@ public class BatteryStatsImpl extends BatteryStats { } public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb, - MeasuredEnergyRetriever energyStatsCb, boolean[] supportedEnergyBuckets, - UserInfoProvider userInfoProvider) { - this(new SystemClocks(), systemDir, handler, cb, energyStatsCb, supportedEnergyBuckets, - userInfoProvider); + MeasuredEnergyRetriever energyStatsCb, UserInfoProvider userInfoProvider) { + this(new SystemClocks(), systemDir, handler, cb, energyStatsCb, userInfoProvider); } private BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler, PlatformIdleStateCallback cb, MeasuredEnergyRetriever energyStatsCb, - boolean[] supportedEnergyBuckets, UserInfoProvider userInfoProvider) { + UserInfoProvider userInfoProvider) { init(clocks); if (systemDir == null) { @@ -10818,10 +10809,6 @@ public class BatteryStatsImpl extends BatteryStats { // Notify statsd that the system is initially not in doze. mDeviceIdleMode = DEVICE_IDLE_MODE_OFF; FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mDeviceIdleMode); - - mGlobalMeasuredEnergyStats = supportedEnergyBuckets == null ? null : - new MeasuredEnergyStats(supportedEnergyBuckets); - mScreenStateAtLastEnergyMeasurement = mScreenState; } @UnsupportedAppUsage @@ -12503,21 +12490,6 @@ public class BatteryStatsImpl extends BatteryStats { } /** - * Get energy consumed (in microjoules) by a set of subsystems from the {@link - * MeasuredEnergyRetriever}, if available. - * - * @return a SparseLongArray that maps consumer id to energy consumed. Returns null if data is - * unavailable. - */ - @Nullable - public MeasuredEnergyArray getEnergyConsumptionDataLocked() { - if (mMeasuredEnergyRetriever == null) { - return null; - } - return mMeasuredEnergyRetriever.getEnergyConsumptionData(); - } - - /** * Read and distribute kernel wake lock use across apps. */ public void updateKernelWakelocksLocked() { @@ -14240,6 +14212,40 @@ public class BatteryStatsImpl extends BatteryStats { mConstants.startObserving(context.getContentResolver()); registerUsbStateReceiver(context); } + /** + * Initialize the measured energy stats data structures. + * + * @param supportedEnergyBuckets boolean array indicating which buckets are currently supported + */ + @GuardedBy("this") + public void initMeasuredEnergyStatsLocked(boolean[] supportedEnergyBuckets) { + boolean supportedBucketMismatch = false; + mScreenStateAtLastEnergyMeasurement = mScreenState; + + if (supportedEnergyBuckets == null) { + if (mGlobalMeasuredEnergyStats != null) { + // Measured energy buckets no longer supported, wipe out the existing data. + supportedBucketMismatch = true; + } + } else if (mGlobalMeasuredEnergyStats == null) { + mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedEnergyBuckets); + return; + } else { + for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + if (mGlobalMeasuredEnergyStats.isEnergyBucketSupported(i) + != supportedEnergyBuckets[i]) { + supportedBucketMismatch = true; + break; + } + } + } + + if (supportedBucketMismatch) { + // Supported energy buckets changed since last boot. + // Existing data is no longer reliable. + resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime()); + } + } @VisibleForTesting public final class Constants extends ContentObserver { @@ -14919,7 +14925,11 @@ public class BatteryStatsImpl extends BatteryStats { mNextMaxDailyDeadlineMs = in.readLong(); mBatteryTimeToFullSeconds = in.readLong(); - MeasuredEnergyStats.readSummaryFromParcel(mGlobalMeasuredEnergyStats, in); + /** + * WARNING: Supported buckets may have changed across boots. Bucket mismatch is handled + * later when {@link #initMeasuredEnergyStatsLocked} is called. + */ + mGlobalMeasuredEnergyStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(in); mStartCount++; @@ -15417,7 +15427,7 @@ public class BatteryStatsImpl extends BatteryStats { out.writeLong(mNextMaxDailyDeadlineMs); out.writeLong(mBatteryTimeToFullSeconds); - MeasuredEnergyStats.writeSummaryToParcel(mGlobalMeasuredEnergyStats, out); + MeasuredEnergyStats.writeSummaryToParcel(mGlobalMeasuredEnergyStats, out, false); mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); mScreenDozeTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); @@ -15742,7 +15752,7 @@ public class BatteryStatsImpl extends BatteryStats { out.writeInt(0); } - MeasuredEnergyStats.writeSummaryToParcel(u.mUidMeasuredEnergyStats, out); + MeasuredEnergyStats.writeSummaryToParcel(u.mUidMeasuredEnergyStats, out, true); final ArrayMap<String, Uid.Wakelock> wakeStats = u.mWakelockStats.getMap(); int NW = wakeStats.size(); diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java index 790d7f7ab694..6860759eea8a 100644 --- a/core/java/com/android/internal/os/WrapperInit.java +++ b/core/java/com/android/internal/os/WrapperInit.java @@ -69,15 +69,16 @@ public class WrapperInit { // Tell the Zygote what our actual PID is (since it only knows about the // wrapper that it directly forked). if (fdNum != 0) { + FileDescriptor fd = new FileDescriptor(); try { - FileDescriptor fd = new FileDescriptor(); fd.setInt$(fdNum); DataOutputStream os = new DataOutputStream(new FileOutputStream(fd)); os.writeInt(Process.myPid()); os.close(); - IoUtils.closeQuietly(fd); } catch (IOException ex) { Slog.d(TAG, "Could not write pid of wrapped process to Zygote pipe.", ex); + } finally { + IoUtils.closeQuietly(fd); } } diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 4512fbac38c1..adebde016bb3 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -2019,6 +2019,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind if (getForeground() != null) { drawableChanged(); } + notifyCaptionHeightChanged(); } } diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java index b644df46c7e3..b744a5d57e98 100644 --- a/core/java/com/android/internal/power/MeasuredEnergyStats.java +++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java @@ -130,12 +130,16 @@ public class MeasuredEnergyStats { * Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} must be updated if summary * parceling changes. */ - private void readSummaryFromParcel(Parcel in) { + private void readSummaryFromParcel(Parcel in, boolean overwriteAvailability) { final int size = in.readInt(); for (int i = 0; i < size; i++) { final int bucket = in.readInt(); final long energyUJ = in.readLong(); - setValueIfSupported(bucket, energyUJ); + if (overwriteAvailability) { + mAccumulatedEnergiesMicroJoules[bucket] = energyUJ; + } else { + setValueIfSupported(bucket, energyUJ); + } } } @@ -143,14 +147,15 @@ public class MeasuredEnergyStats { * Write to summary parcel. * Note: Measured subsystem availability may be different when the summary parcel is read. */ - private void writeSummaryToParcel(Parcel out) { + private void writeSummaryToParcel(Parcel out, boolean skipZero) { final int sizePos = out.dataPosition(); out.writeInt(0); int size = 0; // Write only the supported buckets with non-zero energy. for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { final long energy = mAccumulatedEnergiesMicroJoules[i]; - if (energy <= 0) continue; + if (energy < 0) continue; + if (energy == 0 && skipZero) continue; out.writeInt(i); out.writeLong(mAccumulatedEnergiesMicroJoules[i]); @@ -197,16 +202,19 @@ public class MeasuredEnergyStats { } /** - * Populate a MeasuredEnergyStats from a parcel. If the stats is null, consume and - * ignore the parcelled data. + * Create a MeasuredEnergyStats object from a summary parcel. + * + * @return a new MeasuredEnergyStats object as described. + * Returns null if the parcel indicates there is no data to populate. */ - public static void readSummaryFromParcel(@Nullable MeasuredEnergyStats stats, Parcel in) { + public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(Parcel in) { // Check if any MeasuredEnergyStats exists on the parcel - if (in.readInt() == 0) return; + if (in.readInt() == 0) return null; - // If stats is null, create a placeholder MeasuredEnergyStats to consume the parcel data - final MeasuredEnergyStats mes = stats != null ? stats : new MeasuredEnergyStats(); - mes.readSummaryFromParcel(in); + final MeasuredEnergyStats stats = + new MeasuredEnergyStats(new boolean[NUMBER_ENERGY_BUCKETS]); + stats.readSummaryFromParcel(in, true); + return stats; } /** @@ -227,12 +235,12 @@ public class MeasuredEnergyStats { if (template == null) { // Nothing supported now. Create placeholder object just to consume the parcel data. final MeasuredEnergyStats mes = new MeasuredEnergyStats(); - mes.readSummaryFromParcel(in); + mes.readSummaryFromParcel(in, false); return null; } final MeasuredEnergyStats stats = createFromTemplate(template); - stats.readSummaryFromParcel(in); + stats.readSummaryFromParcel(in, false); if (stats.containsInterestingData()) { return stats; } else { @@ -253,13 +261,13 @@ public class MeasuredEnergyStats { * Write a MeasuredEnergyStats to a parcel. If the stats is null, just write a 0. */ public static void writeSummaryToParcel(@Nullable MeasuredEnergyStats stats, - Parcel dest) { + Parcel dest, boolean skipZero) { if (stats == null) { dest.writeInt(0); return; } dest.writeInt(1); - stats.writeSummaryToParcel(dest); + stats.writeSummaryToParcel(dest, skipZero); } /** Reset accumulated energy. */ diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index b9c2fd532d0f..200e0dd6e65b 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -41,7 +41,6 @@ oneway interface IStatusBar void showWirelessChargingAnimation(int batteryLevel); - void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive); void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition, boolean showImeSwitcher, boolean isMultiClientImeEnabled); void setWindowState(int display, int window, int state); @@ -167,7 +166,7 @@ oneway interface IStatusBar void onRecentsAnimationStateChanged(boolean running); /** - * Notifies System UI side of system bar appearance change on the specified display. + * Notifies System UI side of system bar attribute change on the specified display. * * @param displayId the ID of the display to notify * @param appearance the appearance of the focused window. The light top bar appearance is not @@ -177,9 +176,12 @@ oneway interface IStatusBar * bar, that the bar can have partial appearances in corresponding * stacks. * @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME. + * @param behavior the behavior of the focused window. + * @param isFullscreen whether any of status or navigation bar is requested invisible. */ - void onSystemBarAppearanceChanged(int displayId, int appearance, - in AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme); + void onSystemBarAttributesChanged(int displayId, int appearance, + in AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + int behavior, boolean isFullscreen); /** * Notifies System UI to show transient bars. The transient bars are system bars, e.g., status diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java index 9095f05543da..8fb2f9cd8bf9 100644 --- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java +++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java @@ -38,14 +38,14 @@ public final class RegisterStatusBarResult implements Parcelable { public final int mDisabledFlags2; // switch[6] public final IBinder mImeToken; public final boolean mNavbarColorManagedByIme; + public final int mBehavior; public final boolean mAppFullscreen; - public final boolean mAppImmersive; public final int[] mTransientBarTypes; public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1, int appearance, AppearanceRegion[] appearanceRegions, int imeWindowVis, int imeBackDisposition, boolean showImeSwitcher, int disabledFlags2, IBinder imeToken, - boolean navbarColorManagedByIme, boolean appFullscreen, boolean appImmersive, + boolean navbarColorManagedByIme, int behavior, boolean appFullscreen, @NonNull int[] transientBarTypes) { mIcons = new ArrayMap<>(icons); mDisabledFlags1 = disabledFlags1; @@ -57,8 +57,8 @@ public final class RegisterStatusBarResult implements Parcelable { mDisabledFlags2 = disabledFlags2; mImeToken = imeToken; mNavbarColorManagedByIme = navbarColorManagedByIme; + mBehavior = behavior; mAppFullscreen = appFullscreen; - mAppImmersive = appImmersive; mTransientBarTypes = transientBarTypes; } @@ -79,8 +79,8 @@ public final class RegisterStatusBarResult implements Parcelable { dest.writeInt(mDisabledFlags2); dest.writeStrongBinder(mImeToken); dest.writeBoolean(mNavbarColorManagedByIme); + dest.writeInt(mBehavior); dest.writeBoolean(mAppFullscreen); - dest.writeBoolean(mAppImmersive); dest.writeIntArray(mTransientBarTypes); } @@ -103,13 +103,13 @@ public final class RegisterStatusBarResult implements Parcelable { final int disabledFlags2 = source.readInt(); final IBinder imeToken = source.readStrongBinder(); final boolean navbarColorManagedByIme = source.readBoolean(); + final int behavior = source.readInt(); final boolean appFullscreen = source.readBoolean(); - final boolean appImmersive = source.readBoolean(); final int[] transientBarTypes = source.createIntArray(); return new RegisterStatusBarResult(icons, disabledFlags1, appearance, appearanceRegions, imeWindowVis, imeBackDisposition, showImeSwitcher, - disabledFlags2, imeToken, navbarColorManagedByIme, appFullscreen, - appImmersive, transientBarTypes); + disabledFlags2, imeToken, navbarColorManagedByIme, behavior, + appFullscreen, transientBarTypes); } @Override diff --git a/core/java/com/android/internal/util/LocationPermissionChecker.java b/core/java/com/android/internal/util/LocationPermissionChecker.java index 59c0c0009640..d67bd7a853c8 100644 --- a/core/java/com/android/internal/util/LocationPermissionChecker.java +++ b/core/java/com/android/internal/util/LocationPermissionChecker.java @@ -24,6 +24,7 @@ import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; import android.location.LocationManager; +import android.net.NetworkStack; import android.os.Binder; import android.os.Build; import android.os.UserHandle; @@ -147,6 +148,13 @@ public class LocationPermissionChecker { int uid, @Nullable String message) { checkPackage(uid, pkgName); + // Apps with NETWORK_SETTINGS, NETWORK_SETUP_WIZARD, NETWORK_STACK & MAINLINE_NETWORK_STACK + // are granted a bypass. + if (checkNetworkSettingsPermission(uid) || checkNetworkSetupWizardPermission(uid) + || checkNetworkStackPermission(uid) || checkMainlineNetworkStackPermission(uid)) { + return SUCCEEDED; + } + // Location mode must be enabled if (!isLocationModeEnabled()) { return ERROR_LOCATION_MODE_OFF; @@ -259,4 +267,37 @@ public class LocationPermissionChecker { // We don't care about pid, pass in -1 return mContext.checkPermission(permissionType, -1, uid); } + + /** + * Returns true if the |uid| holds NETWORK_SETTINGS permission. + */ + public boolean checkNetworkSettingsPermission(int uid) { + return getUidPermission(android.Manifest.permission.NETWORK_SETTINGS, uid) + == PackageManager.PERMISSION_GRANTED; + } + + /** + * Returns true if the |uid| holds NETWORK_SETUP_WIZARD permission. + */ + public boolean checkNetworkSetupWizardPermission(int uid) { + return getUidPermission(android.Manifest.permission.NETWORK_SETUP_WIZARD, uid) + == PackageManager.PERMISSION_GRANTED; + } + + /** + * Returns true if the |uid| holds NETWORK_STACK permission. + */ + public boolean checkNetworkStackPermission(int uid) { + return getUidPermission(android.Manifest.permission.NETWORK_STACK, uid) + == PackageManager.PERMISSION_GRANTED; + } + + /** + * Returns true if the |uid| holds MAINLINE_NETWORK_STACK permission. + */ + public boolean checkMainlineNetworkStackPermission(int uid) { + return getUidPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, uid) + == PackageManager.PERMISSION_GRANTED; + } + } diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp index 0fb29111d043..a9db91be1d5b 100644 --- a/core/jni/android_os_HwBlob.cpp +++ b/core/jni/android_os_HwBlob.cpp @@ -257,7 +257,17 @@ jobject JHwBlob::NewObject(JNIEnv *env, size_t size) { // XXX Again cannot refer to gFields.constructID because InitClass may // not have been called yet. - return env->NewObject(clazz.get(), constructID, size); + // Cases: + // - this originates from another process (something so large should not fit + // in the binder buffer, and it should be rejected by the binder driver) + // - if this is used in process, this code makes too many heap copies (in + // order to retrofit HIDL's scatter-gather format to java types) to + // justify passing such a large amount of data over this path. So the + // alternative (updating the constructor and other code to accept other + // types, should also probably not be taken in this case). + CHECK_LE(size, std::numeric_limits<jint>::max()); + + return env->NewObject(clazz.get(), constructID, static_cast<jint>(size)); } } // namespace android diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 2ff474b140e0..4dc81214b95e 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -1784,7 +1784,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE; break; } - android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level)); + mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, heap_tagging_level); // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime. runtime_flags &= ~RuntimeFlags::MEMORY_TAG_LEVEL_MASK; diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto index fa046c6593af..d3c2d31779db 100644 --- a/core/proto/android/server/activitymanagerservice.proto +++ b/core/proto/android/server/activitymanagerservice.proto @@ -626,6 +626,7 @@ message ActivityManagerServiceDumpProcessesProto { optional int32 target_uid = 1; optional int64 duration_ms = 2; optional string tag = 3; + optional int32 type = 4; } repeated PendingTempWhitelist pending_temp_whitelist = 26; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 51fb264cfeb8..253aece16fbf 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -617,6 +617,9 @@ <protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_UNAVAILABLE" /> <protected-broadcast android:name="com.android.server.pm.DISABLE_QUIET_MODE_AFTER_UNLOCK" /> + <protected-broadcast android:name="android.intent.action.PROFILE_ACCESSIBLE" /> + <protected-broadcast android:name="android.intent.action.PROFILE_INACCESSIBLE" /> + <protected-broadcast android:name="com.android.server.retaildemo.ACTION_RESET_DEMO" /> <protected-broadcast android:name="android.intent.action.DEVICE_LOCKED_CHANGED" /> @@ -2676,11 +2679,11 @@ The app can check whether it has this authorization by calling {@link android.provider.Settings#canDrawOverlays Settings.canDrawOverlays()}. - <p>Protection level: signature|appop|installer|pre23|development|recents --> + <p>Protection level: signature|appop|preinstalled|pre23|development --> <permission android:name="android.permission.SYSTEM_ALERT_WINDOW" android:label="@string/permlab_systemAlertWindow" android:description="@string/permdesc_systemAlertWindow" - android:protectionLevel="signature|appop|installer|pre23|development|recents" /> + android:protectionLevel="signature|appop|preinstalled|pre23|development" /> <!-- @SystemApi @hide Allows an application to create windows using the type {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY}, @@ -3105,7 +3108,7 @@ <!-- @SystemApi Allows an application to read system update info. @hide --> <permission android:name="android.permission.READ_SYSTEM_UPDATE_INFO" - android:protectionLevel="signature" /> + android:protectionLevel="signature|privileged" /> <!-- Allows the system to bind to an application's task services @hide --> @@ -3160,6 +3163,11 @@ <permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" android:protectionLevel="signature|privileged" /> + <!-- Allows an application to set, update and remove the credential management app. + @hide --> + <permission android:name="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP" + android:protectionLevel="signature" /> + <!-- ========================================= --> <!-- Permissions for special development tools --> <!-- ========================================= --> @@ -3612,6 +3620,15 @@ <permission android:name="android.permission.BIND_TRANSLATION_SERVICE" android:protectionLevel="signature" /> + <!-- @SystemApi Allows apps to use ui translation functions. + <p>Protection level: signature|privileged + <p> TODO(b/177703453): Restrict to a specific Role instead of allowing access to any + privileged app. + @hide Not for use by third-party applications. + --> + <permission android:name="android.permission.MANAGE_UI_TRANSLATION" + android:protectionLevel="signature|privileged" /> + <!-- Must be required by a android.service.contentsuggestions.ContentSuggestionsService, to ensure that only the system can bind to it. @SystemApi @hide This is not a third-party API (intended for OEMs and system apps). diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 4828f3f3e7be..bd017fde3ae6 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Inkomender beller-ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Uitgaande beller-ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID van gekoppelde lyn"</string> <string name="ColrMmi" msgid="5889782479745764278">"Beperking op ID van gekoppelde lyn"</string> <string name="CfMmi" msgid="8390012691099787178">"Oproepaanstuur"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Net Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g>-oorkruis-SIM-oproepe"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nie aangestuur nie"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> na <xliff:g id="TIME_DELAY">{2}</xliff:g> sekondes"</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 9d87ddc0649e..4955e472f631 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"የገቢ ደዋይID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"የወጪ ጥሪID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"የተገናኘ መስመር መታወቂያ"</string> <string name="ColrMmi" msgid="5889782479745764278">"የተገናኘ መስመር መታወቂያ ገደብ"</string> <string name="CfMmi" msgid="8390012691099787178">"ጥሪ ማስተላለፍ"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi ብቻ"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> የሲም መካከል የሚደረግ ጥሪ"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>፡አልተላለፈም"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>፡<xliff:g id="DIALING_NUMBER">{1}</xliff:g> ከ<xliff:g id="TIME_DELAY">{2}</xliff:g> ሰከንዶች በኋላ"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 2a6d37956b9a..770c3046dbbc 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -61,7 +61,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"معرف المتصل الوارد"</string> - <string name="ClirMmi" msgid="4702929460236547156">"معرف المتصل الصادر"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"معرّف الخط المتصل"</string> <string name="ColrMmi" msgid="5889782479745764278">"تقييد معرّف الخط المتصل"</string> <string name="CfMmi" msgid="8390012691099787178">"إعادة توجيه المكالمة"</string> @@ -152,8 +153,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi فقط"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> الاتصال عبر شرائح SIM"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: لم تتم إعادة التوجيه"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> بعد <xliff:g id="TIME_DELAY">{2}</xliff:g> ثانية"</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index aab590667b7b..2e23ee422ee8 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"অন্তৰ্গামী কলাৰ আইডি"</string> - <string name="ClirMmi" msgid="4702929460236547156">"বহিৰ্গামী কলাৰ আইডি"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"সংযোজিত লাইন আইডি"</string> <string name="ColrMmi" msgid="5889782479745764278">"সংযোজিত লাইন আইডিৰ সীমাবদ্ধতা"</string> <string name="CfMmi" msgid="8390012691099787178">"কল ফৰৱাৰ্ডিং"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"কোৱল ৱাই-ফাই"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> ক্ৰছ-ছিম কলিং"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ফৰৱাৰ্ড কৰা নহ\'ল"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> ছেকেণ্ডৰ পাছত"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index bcc9fe2882fc..475fa7d560df 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Gələn çağrı kimliyi"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Gedən çağrı kimliyi"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Qoşulmuş Xətt ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"Qoşulmuş Xətt ID Məhdudluğu"</string> <string name="CfMmi" msgid="8390012691099787178">"Zəng yönləndirmə"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Yalnız Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Çarpaz SİM Zəngi"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Yönləndirilmədi"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> saniyə sonra"</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 753107737363..637804544697 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -58,7 +58,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Dolazni ID pozivaoca"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Odlazni ID pozivaoca"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID povezane linije"</string> <string name="ColrMmi" msgid="5889782479745764278">"Ograničenje ID-a povezane linije"</string> <string name="CfMmi" msgid="8390012691099787178">"Preusmeravanje poziva"</string> @@ -149,8 +150,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo WiFi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Pozivi sa više SIM kartica za operatera <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nije prosleđeno"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> nakon <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunde/i"</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 93e0802b498c..80c071c2ebde 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -59,7 +59,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Ідэнтыфікатар АВН"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Ідэнтыфікатар АВН"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Ідэнтыфікатар падлучанай лініі"</string> <string name="ColrMmi" msgid="5889782479745764278">"Абмежаванне ідэнтыфікатара падлучанай лініі"</string> <string name="CfMmi" msgid="8390012691099787178">"Пераадрасацыя выкліку"</string> @@ -150,8 +151,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Толькі Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Тэлефанія паміж SIM-картамі"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не пераадрасоўваецца"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> праз <xliff:g id="TIME_DELAY">{2}</xliff:g> с."</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 08cab8d3b044..6dd7b6a68301 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Идентификация на вх. обаждания"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Идентификация на изходящите повиквания"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Идентификация на свързаната линия"</string> <string name="ColrMmi" msgid="5889782479745764278">"Ограничение за идентификацията на свързаната линия"</string> <string name="CfMmi" msgid="8390012691099787178">"Пренасочване на повиквания"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Само Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Обаждания през друга SIM карта от <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Не е пренасочено"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> след <xliff:g id="TIME_DELAY">{2}</xliff:g> секунди"</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index f3ad8daf6cfc..ff9b0035c963 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"আগত কলার আইডি"</string> - <string name="ClirMmi" msgid="4702929460236547156">"আউটগোয়িং কলার আইডি"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"সংযুক্ত লাইন ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"সংযুক্ত লাইন ID-র বিধিনিষেধ"</string> <string name="CfMmi" msgid="8390012691099787178">"কল ফরওয়ার্ড করা"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"শুধুমাত্র ওয়াই-ফাই"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> ক্রস সিম কল করা"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ফরওয়ার্ড করা হয়নি"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> সেকেন্ড পরে"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index d080d05f0464..d2a4f7d94c9e 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -58,7 +58,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ID dolaznog poziva"</string> - <string name="ClirMmi" msgid="4702929460236547156">"ID odlaznog poziva"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Identifikacija povezane linije"</string> <string name="ColrMmi" msgid="5889782479745764278">"Ograničenje identifikacije povezane linije"</string> <string name="CfMmi" msgid="8390012691099787178">"Prosljeđivanje poziva"</string> @@ -149,8 +150,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo WiFi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> – pozivanje na različitim SIM-ovima"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nije proslijeđen"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> za <xliff:g id="TIME_DELAY">{2}</xliff:g> sekundi"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index f7c27bcd20c8..47be22060dd6 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Identificador de trucada (trucada entrant)"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Identificador de trucada (trucada de sortida)"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Identificador de la línia connectada"</string> <string name="ColrMmi" msgid="5889782479745764278">"Restricció de l\'identificador de la línia connectada"</string> <string name="CfMmi" msgid="8390012691099787178">"Desviació de trucades"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Només Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Trucades entre targetes SIM de l\'operador <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: no s\'ha desviat"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> després de <xliff:g id="TIME_DELAY">{2}</xliff:g> segons"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 16397185f8bb..e7e4cc95bfd9 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -59,7 +59,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Příchozí ID volajícího"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Odchozí ID volajícího"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID připojené linky"</string> <string name="ColrMmi" msgid="5889782479745764278">"Omezení ID připojené linky"</string> <string name="CfMmi" msgid="8390012691099787178">"Přesměrování hovorů"</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 7fb204251945..47416cf9d73f 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI-nummer"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Indgående opkalds-id"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Udgående opkalds-id"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Id for opkaldsmodtager"</string> <string name="ColrMmi" msgid="5889782479745764278">"Id for opkaldsmodtager er skjult"</string> <string name="CfMmi" msgid="8390012691099787178">"Viderestilling af opkald"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Kun Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Opkald på tværs af SIM-kort"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ikke viderestillet"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> efter <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunder"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 2e443e87ae41..54e9dfd06fcb 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Anrufer-ID für eingehenden Anruf"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Anrufer-ID für ausgehenden Anruf"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID der verbundenen Leitung"</string> <string name="ColrMmi" msgid="5889782479745764278">"Beschränkung für ID der verbundenen Leitung"</string> <string name="CfMmi" msgid="8390012691099787178">"Rufweiterleitung"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index e056b981e78d..b759be6c787a 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Εισερχόμενη αναγνώριση κλήσης"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Εξερχόμενη αναγνώριση κλήσης"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Αναγνωριστικό συνδεδεμένης γραμμής"</string> <string name="ColrMmi" msgid="5889782479745764278">"Περιορισμός αναγνωριστικού συνδεδεμένης πρόσβασης"</string> <string name="CfMmi" msgid="8390012691099787178">"Προώθηση κλήσεων"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Μόνο Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Κλήση με πολλές SIM <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Δεν προωθήθηκε"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> μετά από <xliff:g id="TIME_DELAY">{2}</xliff:g> δευτερόλεπτα"</string> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 88519df24e96..7c7c1786f71c 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Incoming Caller ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Outgoing Caller ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Connected Line ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"Connected Line ID Restriction"</string> <string name="CfMmi" msgid="8390012691099787178">"Call forwarding"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi only"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Cross-SIM calling"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Not forwarded"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> after <xliff:g id="TIME_DELAY">{2}</xliff:g> seconds"</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 1baa7f9e805f..0cbbb018a082 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Incoming Caller ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Outgoing Caller ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Connected Line ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"Connected Line ID Restriction"</string> <string name="CfMmi" msgid="8390012691099787178">"Call forwarding"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi only"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Cross-SIM calling"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Not forwarded"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> after <xliff:g id="TIME_DELAY">{2}</xliff:g> seconds"</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 687afaedfb6e..e923d6560801 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Incoming Caller ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Outgoing Caller ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Connected Line ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"Connected Line ID Restriction"</string> <string name="CfMmi" msgid="8390012691099787178">"Call forwarding"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi only"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Cross-SIM calling"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Not forwarded"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> after <xliff:g id="TIME_DELAY">{2}</xliff:g> seconds"</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index db34264011ea..6e172426351b 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Incoming Caller ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Outgoing Caller ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Connected Line ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"Connected Line ID Restriction"</string> <string name="CfMmi" msgid="8390012691099787178">"Call forwarding"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi only"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Cross-SIM calling"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Not forwarded"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> after <xliff:g id="TIME_DELAY">{2}</xliff:g> seconds"</string> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index 7d1d04d9f3d5..759fe0109685 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Incoming Caller ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Outgoing Caller ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Connected Line ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"Connected Line ID Restriction"</string> <string name="CfMmi" msgid="8390012691099787178">"Call forwarding"</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 528f7b966eac..64bc3d118c69 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Identificador de llamadas entrantes"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Identificador de llamadas salientes"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID de línea conectada"</string> <string name="ColrMmi" msgid="5889782479745764278">"Restricción de ID de línea conectada"</string> <string name="CfMmi" msgid="8390012691099787178">"Desvío de llamadas"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Solo Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Llamadas entre tarjetas SIM de <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: no se ha remitido"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> después de <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 9c0ab18fe4d5..08555e8da524 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ID de emisor de llamada entrante"</string> - <string name="ClirMmi" msgid="4702929460236547156">"ID de emisor de llamada saliente"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID de línea conectada"</string> <string name="ColrMmi" msgid="5889782479745764278">"Restricción de ID de línea conectada"</string> <string name="CfMmi" msgid="8390012691099787178">"Desvío de llamadas"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Solo Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Llamadas entre tarjetas SIM de <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: No desviada"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> transcurridos <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 8de5734d25a5..4818589d0f1c 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Sissetuleva kõne helistaja ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Väljuva kõne helistaja ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Ühendatud liini ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"Ühendatud liini ID piiramine"</string> <string name="CfMmi" msgid="8390012691099787178">"Kõnede suunamine"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Ainult WiFi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> – helistamine mitme SIM-kaardi kaudu"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: pole suunatud"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> sekundi pärast"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 5a944f3ce5c2..39fc77c79861 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI zk."</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Sarrerako deien identifikazio-zerbitzua"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Irteerako deien identifikazio-zerbitzua"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Konektatutako linearen IDa"</string> <string name="ColrMmi" msgid="5889782479745764278">"Konektatutako linearen ID murriztapena"</string> <string name="CfMmi" msgid="8390012691099787178">"Dei-desbideratzea"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wifi-sarea soilik"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> operadorearen beste SIM txartel batetik deitzeko aukera"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ez da desbideratu"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> zenbakira <xliff:g id="TIME_DELAY">{2}</xliff:g> segundotan"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index be27d7061ff4..935d67954d5b 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"شناسه تماسگیرنده ورودی"</string> - <string name="ClirMmi" msgid="4702929460236547156">"شناسه تماسگیرنده خروجی"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"شناسه خط متصل"</string> <string name="ColrMmi" msgid="5889782479745764278">"محدودیت شناسه خط متصل"</string> <string name="CfMmi" msgid="8390012691099787178">"هدایت تماس"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"فقط Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"تماس بین سیمکارت <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: هدایت نشده"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> پس از <xliff:g id="TIME_DELAY">{2}</xliff:g> ثانیه"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index dc2af5af505c..09f289accfcc 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI-koodi"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Soittajan tunnus"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Soittajan tunnus"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Yhteyslinjan tunnus"</string> <string name="ColrMmi" msgid="5889782479745764278">"Yhteyslinjan tunnuksen rajoitus"</string> <string name="CfMmi" msgid="8390012691099787178">"Soitonsiirto"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Vain Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"SIM-korttien väliset puhelut: <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ei siirretty"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunnin päästä"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index c4bf3f7f23a9..6bedfcad014d 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"Code IIEM"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Numéro de l\'appelant (entrant)"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Numéro de l\'appelant (sortant)"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Identifiant de la ligne connectée"</string> <string name="ColrMmi" msgid="5889782479745764278">"Restriction d\'identifiant de la ligne connectée"</string> <string name="CfMmi" msgid="8390012691099787178">"Transfert d\'appel"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi seulement"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Appels multiSIM avec <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : non transféré"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g> au bout de <xliff:g id="TIME_DELAY">{2}</xliff:g> secondes"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 998845f88a65..d8edc5daf1d7 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"Code IMEI"</string> <string name="meid" msgid="3291227361605924674">"Code MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Numéro de l\'appelant (entrant)"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Numéro de l\'appelant (sortant)"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Identifiant de la ligne connectée"</string> <string name="ColrMmi" msgid="5889782479745764278">"Restriction d\'identifiant de la ligne connectée"</string> <string name="CfMmi" msgid="8390012691099787178">"Transfert d\'appel"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi uniquement"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Appels par cartes SIM croisées <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : non transféré"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g> au bout de <xliff:g id="TIME_DELAY">{2}</xliff:g> secondes"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 8548506512ed..76ab6abc11f4 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Identificador de chamada entrante"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Identificador de chamada saínte"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID de liña conectada"</string> <string name="ColrMmi" msgid="5889782479745764278">"Restrición de ID de liña conectada"</string> <string name="CfMmi" msgid="8390012691099787178">"Desvío de chamadas"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Só por wifi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Chamadas doutra SIM a través desta (<xliff:g id="SPN">%s</xliff:g>)"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: non desviada"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> tras <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 4f27856a2ad2..a591d319e5ff 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"આવનાર કૉલર ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"આઉટગોઇંગ કૉલર ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"કનેક્ટ કરેલ લાઇન ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"કનેક્ટ કરેલ લાઇન ID પ્રતિબંધ"</string> <string name="CfMmi" msgid="8390012691099787178">"કૉલ ફોર્વર્ડિંગ"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index f374b9610fc1..a094117c9dea 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"आईएमईआई"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"इनकमिंग कॉलर आईडी"</string> - <string name="ClirMmi" msgid="4702929460236547156">"आउटगोइंग कॉलर आईडी"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"कनेक्ट किया गया लाइन आईडी"</string> <string name="ColrMmi" msgid="5889782479745764278">"कनेक्ट किया गया लाइन आईडी प्रतिबंध"</string> <string name="CfMmi" msgid="8390012691099787178">"कॉल आगे भेजना"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"केवल वाई-फ़ाई"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> क्रॉस सिम कॉलिंग"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: अग्रेषित नहीं किया गया"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> सेकंड के बाद"</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 981fff4974ca..aebab8c9f5cc 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -58,7 +58,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ID dolaznog pozivatelja"</string> - <string name="ClirMmi" msgid="4702929460236547156">"ID izlaznog pozivatelja"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID povezane linije"</string> <string name="ColrMmi" msgid="5889782479745764278">"Ograničenje ID-a povezane linije"</string> <string name="CfMmi" msgid="8390012691099787178">"Preusmjeravanje poziva"</string> @@ -149,8 +150,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Preusmjeravanje poziva na drugi SIM"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nije proslijeđeno"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> nakon <xliff:g id="TIME_DELAY">{2}</xliff:g> s"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 09ee61aa9007..efdf842d4c96 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Beérkező hívóazonosító"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Kimenő hívóazonosító"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Összekapcsolt sorazonosító"</string> <string name="ColrMmi" msgid="5889782479745764278">"Összekapcsolt sorazonosító korlátozása"</string> <string name="CfMmi" msgid="8390012691099787178">"Hívásátirányítás"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Csak Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> SIM-eken keresztüli hívás"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: nincs átirányítva"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> másodperc után"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 0539da1f253f..29803b8e7b71 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Մուտքային զանգողի ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Ելքային զանգողի ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Կապված տողի ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"Կապված տողի ID-ի սահմանափակում"</string> <string name="CfMmi" msgid="8390012691099787178">"Զանգի վերահասցեավորում"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Միայն Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> SIM քարտերով խաչաձև զանգեր"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>. Չի վերահասցեավորվել"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>. <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>. <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> վայրկյանից"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 969145f5bb26..510621b658fd 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Nomor Penelepon Masuk"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Nomor Penelepon Keluar"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID Saluran yang Tersambung"</string> <string name="ColrMmi" msgid="5889782479745764278">"Batasan ID Saluran yang Tersambung"</string> <string name="CfMmi" msgid="8390012691099787178">"Penerusan panggilan"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Khusus Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Panggilan Lintas-SIM <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Tidak diteruskan"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> setelah <xliff:g id="TIME_DELAY">{2}</xliff:g> detik"</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 7c7c4eb15ac6..802feeeb7791 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Númerabirting innhringinga"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Númerabirting úthringinga"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Auðkenni tengdrar línu"</string> <string name="ColrMmi" msgid="5889782479745764278">"Auðkennistakmörkun tengdrar línu"</string> <string name="CfMmi" msgid="8390012691099787178">"Símtalsflutningur"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi eingöngu"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Skipt á milli SIM-korta"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ekki áframsent"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> eftir <xliff:g id="TIME_DELAY">{2}</xliff:g> sekúndur"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 170349351233..2c017766df81 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ID chiamante in entrata"</string> - <string name="ClirMmi" msgid="4702929460236547156">"ID chiamante in uscita"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID linea connessa"</string> <string name="ColrMmi" msgid="5889782479745764278">"Limitazione ID linea connessa"</string> <string name="CfMmi" msgid="8390012691099787178">"Deviazione chiamate"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Solo Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Chiamate tramite più SIM <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: inoltro non effettuato"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g><xliff:g id="DIALING_NUMBER">{1}</xliff:g> dopo <xliff:g id="TIME_DELAY">{2}</xliff:g> secondi"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 8a73d70112ab..2bdabc81db59 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -59,7 +59,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"זיהוי מתקשר של שיחה נכנסת"</string> - <string name="ClirMmi" msgid="4702929460236547156">"זיהוי מתקשר בשיחה יוצאת"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"מזהה של קו מחובר"</string> <string name="ColrMmi" msgid="5889782479745764278">"הגבלה של מזהה קו מחובר"</string> <string name="CfMmi" msgid="8390012691099787178">"העברת שיחות"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 263c87e77d36..cf493c00621e 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"着信時の発信者番号"</string> - <string name="ClirMmi" msgid="4702929460236547156">"発信者番号"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"接続回線ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"接続回線IDの制限"</string> <string name="CfMmi" msgid="8390012691099787178">"着信転送"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fiのみ"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Cross-SIM 通話"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:転送できません"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g> (<xliff:g id="TIME_DELAY">{2}</xliff:g>秒後)"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 7b616b4130ea..9de08bc6e090 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"შემომავალი ზარის აბონენტის ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"გამავალი მრეკავის ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"დაუკავშირდა Line ID-ს"</string> <string name="ColrMmi" msgid="5889782479745764278">"დაუკავშირდა Line ID Restriction-ს"</string> <string name="CfMmi" msgid="8390012691099787178">"ზარის გადამისამართება"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"მხოლოდ Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> SIM-თაშორისი დარეკვა"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: არ არის გადამისამართებული"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> წამის შემდეგ"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 96cf9025551a..f38be017d462 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI (Халықаралық мобильдік құрылғы анықтағышы)"</string> <string name="meid" msgid="3291227361605924674">"MEID (ұялы құрылғы анықтағыш)"</string> <string name="ClipMmi" msgid="4110549342447630629">"Келген қоңырау шалушының жеке анықтағышы"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Шыққан қоңырау шалушының жеке анықтағышы"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Қосылған желі идентификаторы"</string> <string name="ColrMmi" msgid="5889782479745764278">"Қосылған желі идентификаторын шектеу"</string> <string name="CfMmi" msgid="8390012691099787178">"Қоңырауды басқа нөмірге бағыттау"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Тек Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> SIM карталары арасында қоңырау шалу"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Басқа нөмірге бағытталмады"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> секундтан кейін"</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 8db1191b8db4..2a704494af79 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"លេខសម្គាល់អ្នកហៅចូល"</string> - <string name="ClirMmi" msgid="4702929460236547156">"លេខសម្គាល់អ្នកហៅចេញ"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"បានភ្ជាប់លេខសម្គាល់បន្ទាត់"</string> <string name="ColrMmi" msgid="5889782479745764278">"បានភ្ជាប់ការដាក់កម្រិតលេខសម្គាល់បន្ទាត់"</string> <string name="CfMmi" msgid="8390012691099787178">"បញ្ជូនការហៅបន្ត"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi តែប៉ុណ្ណោះ"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"ការហៅទូរសព្ទឆ្លងស៊ីមតាមរយៈ <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> ៖ មិនបានបញ្ជូនបន្ត"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> បន្ទាប់ពី <xliff:g id="TIME_DELAY">{2}</xliff:g> វិនាទី"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index 8bd2f49a988d..9fc9f369a258 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ಒಳಬರುವ ಕರೆಮಾಡುವವರ ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"ಹೊರಹೋಗುವ ಕರೆಮಾಡುವವರ ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ಲೈನ್ ID ಗೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ"</string> <string name="ColrMmi" msgid="5889782479745764278">"ಲೈನ್ ID ನಿರ್ಬಂಧನೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ"</string> <string name="CfMmi" msgid="8390012691099787178">"ಕರೆಯ ರವಾನೆ"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index d812de901d25..fd81606bcfd4 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"발신자 번호"</string> - <string name="ClirMmi" msgid="4702929460236547156">"내 발신 번호"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"환승편 ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"환승편 ID 제한"</string> <string name="CfMmi" msgid="8390012691099787178">"착신전환"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi에서만"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Cross SIM 통화"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: 착신전환 안됨"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g><xliff:g id="TIME_DELAY">{2}</xliff:g>초 후"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index acd46cedcf3f..612193c4259f 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Кирүүчү номурду аныктоо"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Чыгуучу номурду аныктоо"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Туташкан линия ID-си"</string> <string name="ColrMmi" msgid="5889782479745764278">"Туташкан линия ID-син Чектөө"</string> <string name="CfMmi" msgid="8390012691099787178">"Башка номерге багыттоо"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi гана"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> SIM карталарынан кайчылаш чалуу"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Багытталган эмес"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> секунддан кийин"</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index ceee938315b0..760d920d6fe0 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ໝາຍເລກຜູ່ໂທເຂົ້າ"</string> - <string name="ClirMmi" msgid="4702929460236547156">"ໝາຍເລກຜູ່ໂທອອກ"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Line ID ທີ່ເຊື່ອມໂຍງ"</string> <string name="ColrMmi" msgid="5889782479745764278">"ຂໍ້ຈຳກັດ Line ID ທີ່ເຊື່ອມໂຍງ"</string> <string name="CfMmi" msgid="8390012691099787178">"ການໂອນສາຍ"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi ເທົ່ານັ້ນ"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> ການໂທຂ້າມຊິມ"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ບໍ່ຖືກສົ່ງຕໍ່"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> ຫຼັງຈາກ <xliff:g id="TIME_DELAY">{2}</xliff:g> ວິນາທີ"</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index f260ebe47b23..da18b18823b1 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -59,7 +59,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Įeinančio skambintojo ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Išeinančio skambintojo ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Prijungtos eilutės ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"Prijungtos eilutės ID apribojimas"</string> <string name="CfMmi" msgid="8390012691099787178">"Skambučio peradresavimas"</string> @@ -150,8 +151,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Tik „Wi-Fi“"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"„<xliff:g id="SPN">%s</xliff:g>“: skambinimas per SIM korteles"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neperadresuota"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po <xliff:g id="TIME_DELAY">{2}</xliff:g> sek."</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 5fe3733a9064..4fdd570f3e05 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -58,7 +58,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Ienākošā zvana zvanītāja ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Izejošā zvana zvanītāja ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Saistītās līnijas ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"Saistītās līnijas ID ierobežojums"</string> <string name="CfMmi" msgid="8390012691099787178">"Zvanu pāradresācija"</string> @@ -149,8 +150,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Tikai Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g>: zvanīšana, izmantojot dažādas SIM kartes"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: nav pāradresēts"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> pēc <xliff:g id="TIME_DELAY">{2}</xliff:g> sekundes(-ēm)"</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index b7536b427ed8..b18ce234ca78 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ID на дојдовен повикувач"</string> - <string name="ClirMmi" msgid="4702929460236547156">"ID на појдовен повикувач"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID на поврзана линија"</string> <string name="ColrMmi" msgid="5889782479745764278">"Забрана на ID на поврзана линија"</string> <string name="CfMmi" msgid="8390012691099787178">"Проследување повик"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Само Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Повици преку повеќе SIM-картички на <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не е препратено"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> по <xliff:g id="TIME_DELAY">{2}</xliff:g> секунди"</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 5d18f8213cc7..d21ab5a43885 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ഇൻകമിംഗ് വിളിച്ച നമ്പർ"</string> - <string name="ClirMmi" msgid="4702929460236547156">"ഔട്ട്ഗോയിംഗ് വിളിച്ച നമ്പർ"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"കണക്റ്റുചെയ്തിരിക്കുന്ന ലൈൻ ഐഡി"</string> <string name="ColrMmi" msgid="5889782479745764278">"കണക്റ്റുചെയ്തിരിക്കുന്ന ലൈൻ ഐഡി നിയന്ത്രണം"</string> <string name="CfMmi" msgid="8390012691099787178">"കോൾ ഫോർവേഡിംഗ്"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"വൈഫൈ മാത്രം"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> ക്രോസ് സിം കോളിംഗ്"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: കൈമാറിയില്ല"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> നിമിഷത്തിനുശേഷം <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 4b1b1b8b2569..778a15bbec3d 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Дуудлага хийгчийн ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Гарч байгаа дуудлага хийгчийн ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Холбогдсон шугамын ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"Холбогдсон шугамын ID Хязгаарлалт"</string> <string name="CfMmi" msgid="8390012691099787178">"Дуудлага дамжуулах"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Зөвхөн Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> SIM хоорондын дуудлага"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: дамжуулагдаагүй"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> секундын дараа"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index a5577c8aaa70..dc5175b3fb5a 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"येणारा कॉलर आयडी"</string> - <string name="ClirMmi" msgid="4702929460236547156">"केला जाणारा कॉलर आयडी"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"कनेक्ट केलेला रेखा आयडी"</string> <string name="ColrMmi" msgid="5889782479745764278">"कनेक्ट केलेला रेखा आयडी प्रतिबंध"</string> <string name="CfMmi" msgid="8390012691099787178">"कॉल फॉरवर्डिंग"</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index afd49eece2e5..a14ea625fb51 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ID Pemanggil Masuk"</string> - <string name="ClirMmi" msgid="4702929460236547156">"ID Pemanggil Keluar"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID Laluan yang disambungkan"</string> <string name="ColrMmi" msgid="5889782479745764278">"Sekatan ID Laluan yang disambungkan"</string> <string name="CfMmi" msgid="8390012691099787178">"Pemajuan panggilan"</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 657a1d31c65a..f768d17cdc2d 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEIDနံပါတ်"</string> <string name="ClipMmi" msgid="4110549342447630629">"အဝင်ခေါ်ဆိုမှုID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"အထွက်ခေါ်ဆိုခြင်းအိုင်ဒီ"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"လိုင်း ID ချိတ်ဆက်သည်"</string> <string name="ColrMmi" msgid="5889782479745764278">"လိုင်း ID ချိတ်ဆက်မှု ကန့်သတ်ချက်များ"</string> <string name="CfMmi" msgid="8390012691099787178">"အဝင်ခေါ်ဆိုမှုအား ထပ်ဆင့်ပို့ခြင်း"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"ကြိုးမဲ့အင်တာနက် သာလျှင်"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Cross-SIM ခေါ်ဆိုမှု"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ထပ်ဆင့်မပို့နိုင်ပါ"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> နောက် <xliff:g id="TIME_DELAY">{2}</xliff:g> စက္ကန့်"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 8eed7f5e7183..c6831a8756e7 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Inngående nummervisning"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Utgående nummervisning"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Tilkoblet linje-ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"Begrensning for tilkoblet linje-ID"</string> <string name="CfMmi" msgid="8390012691099787178">"Viderekobling"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Bare Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Ringing mellom SIM-kort med <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ikke viderekoblet"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> etter <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunder"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index b813d9e516b2..ff0b00e4135b 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"आगमन कलर ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"बाहिरिने कलर ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"लाइन ID जोडियो"</string> <string name="ColrMmi" msgid="5889782479745764278">"जोडिएको लाइन ID प्रतिबन्ध"</string> <string name="CfMmi" msgid="8390012691099787178">"कल अगाडि बढाउँदै"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi मात्र"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> क्रस SIM कलिङ"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: अगाडि पठाइएको छैन"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> पछि <xliff:g id="TIME_DELAY">{2}</xliff:g> सेकेन्ड"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 9ef66df0064d..4ada7a2774ab 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Inkomende beller-ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Uitgaande beller-ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID van verbonden lijn"</string> <string name="ColrMmi" msgid="5889782479745764278">"Beperking voor ID van verbonden lijn"</string> <string name="CfMmi" msgid="8390012691099787178">"Gesprek doorschakelen"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Alleen wifi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Cross-sim-bellen van <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: niet doorgeschakeld"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> na <xliff:g id="TIME_DELAY">{2}</xliff:g> seconden"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index a2c375973057..54e064dedd52 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ଇନକମିଙ୍ଗ କଲର୍ ଆଇଡି"</string> - <string name="ClirMmi" msgid="4702929460236547156">"ଆଉଟଗୋଇଙ୍ଗ୍ କଲର୍ ଆଇଡି"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ସଂଯୁକ୍ତ ଲାଇନ୍ ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"ସଂଯୁକ୍ତ ଲାଇନ୍ ID କଟକଣା"</string> <string name="CfMmi" msgid="8390012691099787178">"କଲ୍ ଫରୱାର୍ଡିଂ"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 3ceb626a52b4..804b77d27b67 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ਇਨਕਮਿੰਗ ਕਾਲਰ ਆਈ.ਡੀ."</string> - <string name="ClirMmi" msgid="4702929460236547156">"ਆਊਟਗੋਇੰਗ ਕਾਲਰ ਆਈ.ਡੀ."</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ਕਨੈਕਟ ਕੀਤੀ ਲਾਈਨ ਆਈ.ਡੀ."</string> <string name="ColrMmi" msgid="5889782479745764278">"ਕਨੈਕਟ ਕੀਤੀ ਲਾਈਨ ਆਈ.ਡੀ. ਪ੍ਰਤਿਬੰਧ"</string> <string name="CfMmi" msgid="8390012691099787178">"ਕਾਲ ਫਾਰਵਰਡਿੰਗ"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 6849333bd894..8d669c47bc3f 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -59,7 +59,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ID rozmówcy przy połączeniach przychodzących"</string> - <string name="ClirMmi" msgid="4702929460236547156">"ID rozmówcy przy połączeniach wychodzących"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Identyfikator połączonej linii"</string> <string name="ColrMmi" msgid="5889782479745764278">"Ograniczenie identyfikatora połączonej linii"</string> <string name="CfMmi" msgid="8390012691099787178">"Przekierowanie połączeń"</string> @@ -150,8 +151,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Tylko Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Połączenia przez różne karty SIM z <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: nieprzekierowane"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po <xliff:g id="TIME_DELAY">{2}</xliff:g> sekundach"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 84e5a738aa90..9837b881e1a9 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Identificador de chamadas recebidas"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Identificador de chamadas realizadas"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID de linha conectada"</string> <string name="ColrMmi" msgid="5889782479745764278">"Restrição de ID de linha conectada"</string> <string name="CfMmi" msgid="8390012691099787178">"Encaminhamento de chamada"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Somente Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Chamadas entre chips da <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não encaminhado"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> após <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 507d759140bd..ef7ee9853083 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ID do Autor da Chamada"</string> - <string name="ClirMmi" msgid="4702929460236547156">"ID do autor da chamada efetuada"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID de linha ligada"</string> <string name="ColrMmi" msgid="5889782479745764278">"Restrição de ID de linha ligada"</string> <string name="CfMmi" msgid="8390012691099787178">"Encaminhamento de chamadas"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 84e5a738aa90..9837b881e1a9 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Identificador de chamadas recebidas"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Identificador de chamadas realizadas"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID de linha conectada"</string> <string name="ColrMmi" msgid="5889782479745764278">"Restrição de ID de linha conectada"</string> <string name="CfMmi" msgid="8390012691099787178">"Encaminhamento de chamada"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Somente Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Chamadas entre chips da <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não encaminhado"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> após <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 4b1d3ac4e779..68dad1c16cea 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -58,7 +58,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ID apelant de primire"</string> - <string name="ClirMmi" msgid="4702929460236547156">"ID apelant"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID-ul liniei conectate"</string> <string name="ColrMmi" msgid="5889782479745764278">"Restricționarea ID-ului liniei conectate"</string> <string name="CfMmi" msgid="8390012691099787178">"Redirecționarea apelurilor"</string> @@ -149,8 +150,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Numai Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Apelare pe mai multe carduri SIM <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neredirecționată"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> după <xliff:g id="TIME_DELAY">{2}</xliff:g> secunde"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 1588c2b82e0b..03383f9834bd 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -59,7 +59,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Идентификация вызывающего абонента"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Идентификация звонящего абонента"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Идентификатор подключенной линии"</string> <string name="ColrMmi" msgid="5889782479745764278">"Ограничение идентификатора подключенной линии"</string> <string name="CfMmi" msgid="8390012691099787178">"Переадресация вызовов"</string> @@ -150,8 +151,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Только Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Перекрестная работа SIM-карт от \"<xliff:g id="SPN">%s</xliff:g>\""</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не переадресовано"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> через <xliff:g id="TIME_DELAY">{2}</xliff:g> с."</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index db0d5add1e78..cfa9bf1605ae 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"පැමිණෙන අමතන්නාගේ ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"පිටතට යන අමතන්නාගේ ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"සම්බන්ධ කළ Line ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"සම්බන්ධ කළ Line ID සීමා කිරීම්"</string> <string name="CfMmi" msgid="8390012691099787178">"ඇමතුම ඉදිරියට යැවීම"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi පමණයි"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> හරස් SIM ඇමතුම"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ඉදිරියට නොයවන ලදි"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: තත්පර <xliff:g id="TIME_DELAY">{2}</xliff:g> ට පසුව <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 546a64b13b5a..f8dfc2668184 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -59,7 +59,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Prichádzajúca identifikácia volajúceho"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Odchádzajúca identifikácia volajúceho"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID pripojenej linky"</string> <string name="ColrMmi" msgid="5889782479745764278">"Obmedzenie ID pripojenej linky"</string> <string name="CfMmi" msgid="8390012691099787178">"Presmerovanie hovorov"</string> @@ -150,8 +151,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Len Wi‑Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Volanie naprieč SIM kartami"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nepresmerované"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po <xliff:g id="TIME_DELAY">{2}</xliff:g> s"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index a15676d1b9cd..4a5c5dabf160 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -59,7 +59,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ID dohodnega klicatelja"</string> - <string name="ClirMmi" msgid="4702929460236547156">"ID odhodnega klicatelja"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID povezane linije"</string> <string name="ColrMmi" msgid="5889782479745764278">"Omejitev ID-ja povezane linije"</string> <string name="CfMmi" msgid="8390012691099787178">"Preusmerjanje klicev"</string> @@ -150,8 +151,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Klicanje ne glede na kartico SIM operaterja <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ni posredovano"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po toliko sekundah: <xliff:g id="TIME_DELAY">{2}</xliff:g>"</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index cc694fa87095..5ee903183f40 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ID-ja e telefonuesit hyrës"</string> - <string name="ClirMmi" msgid="4702929460236547156">"ID-ja e telefonuesit në dalje"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID-ja e linjës së lidhur"</string> <string name="ColrMmi" msgid="5889782479745764278">"Kufizimi i ID-së së linjës së lidhur"</string> <string name="CfMmi" msgid="8390012691099787178">"Transferimi i telefonatave"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 5cadfd5fed45..a430d56c6864 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -58,7 +58,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Долазни ИД позиваоца"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Одлазни ИД позиваоца"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ИД повезане линије"</string> <string name="ColrMmi" msgid="5889782479745764278">"Ограничење ИД-а повезане линије"</string> <string name="CfMmi" msgid="8390012691099787178">"Преусмеравање позива"</string> @@ -149,8 +150,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Само WiFi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Позиви са више SIM картица за оператера <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Није прослеђено"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> након <xliff:g id="TIME_DELAY">{2}</xliff:g> секунде/и"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index bba8700c614b..186a78a1b561 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI-kod"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Nummerpresentatör för inkommande samtal"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Nummerpresentatör för utgående samtal"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Visning av uppkopplat nummer"</string> <string name="ColrMmi" msgid="5889782479745764278">"Blockera visning av uppkopplat nummer"</string> <string name="CfMmi" msgid="8390012691099787178">"Vidarekoppla samtal"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Endast Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> – samtal mellan SIM-kort"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Vidarebefordras inte"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g><xliff:g id="DIALING_NUMBER">{1}</xliff:g> efter <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunder"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 3525ceb1db8e..cf9496d8377d 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Kitambulisho cha Mpigaji wa Simu Inayoingia"</string> - <string name="ClirMmi" msgid="4702929460236547156">"ID ya Mpigaji simu Inayotoka nje"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Kitambulisho cha Mstari Uliounganishwa"</string> <string name="ColrMmi" msgid="5889782479745764278">"Kizuizi cha Kitambulisho cha Mstari Uliounganishwa"</string> <string name="CfMmi" msgid="8390012691099787178">"Kusambaza simu"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi pekee"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Kipengele cha Kupiga Simu Kupitia SIM Tofauti"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Haijatumiwa mwingine"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> baada ya sekunde <xliff:g id="TIME_DELAY">{2}</xliff:g>"</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 61eac8197edf..f3e5f912a269 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"உள்வரும் அழைப்பாளர் ஐடி"</string> - <string name="ClirMmi" msgid="4702929460236547156">"வெளிசெல்லும் அழைப்பாளர் ஐடி"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"இணைக்கப்பட்ட லைன் ஐடி"</string> <string name="ColrMmi" msgid="5889782479745764278">"இணைக்கப்பட்ட லைன் ஐடியை வரம்பிடல்"</string> <string name="CfMmi" msgid="8390012691099787178">"அழைப்பு திருப்பிவிடுதல்"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"வைஃபை மட்டும்"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> கிராஸ்-சிம் அழைப்பு"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: பகிரப்படவில்லை"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> வினாடிகளுக்குப் பிறகு <xliff:g id="DIALING_NUMBER">{1}</xliff:g> ஐப் பகிர்"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 666e9801b27a..2687aa10bf8a 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ఇన్కమింగ్ కాలర్ ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"అవుట్గోయింగ్ కాలర్ ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"కనెక్ట్ చేయబడిన పంక్తి ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"కనెక్ట్ చేయబడిన పంక్తి ID నియంత్రణ"</string> <string name="CfMmi" msgid="8390012691099787178">"కాల్ ఫార్వర్డింగ్"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi మాత్రమే"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> క్రాస్-SIM కాలింగ్"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ఫార్వార్డ్ చేయబడలేదు"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> సెకన్ల తర్వాత <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 5c6b7df6e9e3..01ffd62eba85 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"หมายเลขผู้โทรเข้า"</string> - <string name="ClirMmi" msgid="4702929460236547156">"หมายเลขผู้โทรออก"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"รหัสสายที่เชื่อมต่อ"</string> <string name="ColrMmi" msgid="5889782479745764278">"ข้อจำกัดรหัสสายที่เชื่อมต่อ"</string> <string name="CfMmi" msgid="8390012691099787178">"การโอนสาย"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi เท่านั้น"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"การโทรข้ามซิม <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ไม่ได้โอนสาย"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> หลังผ่านไป <xliff:g id="TIME_DELAY">{2}</xliff:g> วินาที"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index d27757cdad1c..40968ebe20a9 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Papasok na Caller ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Papalabas na Caller ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Connected Line ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"Paghihigpit sa Connected Line ID"</string> <string name="CfMmi" msgid="8390012691099787178">"Pagpapasa ng tawag"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi lang"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Cross-SIM na Pagtawag sa <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Hindi naipasa"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> pagkatapos ng <xliff:g id="TIME_DELAY">{2}</xliff:g> (na) segundo"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 6c5ea97a5fbc..342ff202d4b2 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Gelen Çağrı Kimliği"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Giden Çağrı Kimliği"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Bağlanılan Hat Kimliği"</string> <string name="ColrMmi" msgid="5889782479745764278">"Bağlanılan Hat Kimliğini Kısıtlama"</string> <string name="CfMmi" msgid="8390012691099787178">"Çağrı yönlendirme"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Yalnızca kablosuz"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Çapraz SIM Araması"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Yönlendirilmedi"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> saniye sonra <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 6ae2e8e318ee..cf2c6f116cd1 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -59,7 +59,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Вхідн. ід. абонента"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Вихід. ід. абонента"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Ідентифікатор під’єднаної лінії"</string> <string name="ColrMmi" msgid="5889782479745764278">"Обмеження ідентифікатора під’єднаної лінії"</string> <string name="CfMmi" msgid="8390012691099787178">"Переадресація виклику"</string> @@ -150,8 +151,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Лише Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> – виклики між SIM-картами"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не переслано"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> після <xliff:g id="TIME_DELAY">{2}</xliff:g> сек."</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index ae7f801a4e00..8f54c2783c88 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ان کمنگ کالر ID"</string> - <string name="ClirMmi" msgid="4702929460236547156">"آؤٹ گوئنگ کالر ID"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"منسلک لائن ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"منسلک لائن ID کی پابندی"</string> <string name="CfMmi" msgid="8390012691099787178">"کال فارورڈنگ"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 9bfb68f0cc6e..a8bc4ba9a7b6 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Kiruvchi raqami"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Chiquvchi raqami"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"Qo‘ng‘iroq qiluvchining raqami"</string> <string name="ColrMmi" msgid="5889782479745764278">"Qo‘ng‘iroq qiluvchining raqamini cheklash"</string> <string name="CfMmi" msgid="8390012691099787178">"Chaqiruvlarni uzatish"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Faqat Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Umumiy sim chaqiruvlar"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Yo‘naltirilmadi"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> soniyadan so‘ng"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index e11291c03ae0..0af241eda374 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"Số gọi đến"</string> - <string name="ClirMmi" msgid="4702929460236547156">"Số gọi đi"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"ID đường kết nối"</string> <string name="ColrMmi" msgid="5889782479745764278">"Giới hạn ID đường kết nối"</string> <string name="CfMmi" msgid="8390012691099787178">"Chuyển tiếp cuộc gọi"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Chỉ Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Gọi bằng nhiều SIM"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Không được chuyển tiếp"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> sau <xliff:g id="TIME_DELAY">{2}</xliff:g> giây"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 694014082b3c..45033052ac5a 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"来电显示"</string> - <string name="ClirMmi" msgid="4702929460236547156">"本机号码"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"连接的线路ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"连接的线路ID限制"</string> <string name="CfMmi" msgid="8390012691099787178">"来电转接"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"仅限 WLAN"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> 跨 SIM 卡通话"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:无法转接"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="TIME_DELAY">{2}</xliff:g>秒后<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 1955b315f7d6..92b007c91dc3 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"來電顯示"</string> - <string name="ClirMmi" msgid="4702929460236547156">"本機號碼"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"連接線識別功能"</string> <string name="ColrMmi" msgid="5889782479745764278">"連接線識別限制"</string> <string name="CfMmi" msgid="8390012691099787178">"來電轉駁"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"只限 Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> 跨 SIM 卡通話"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:尚未轉接"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> 於 <xliff:g id="TIME_DELAY">{2}</xliff:g> 秒後轉接"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index b95974da0443..df2d5ead7200 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"來電顯示"</string> - <string name="ClirMmi" msgid="4702929460236547156">"本機號碼"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"連接的線路 ID"</string> <string name="ColrMmi" msgid="5889782479745764278">"連接的線路 ID 限制"</string> <string name="CfMmi" msgid="8390012691099787178">"來電轉接"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"只限 Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"「<xliff:g id="SPN">%s</xliff:g>」跨 SIM 卡通話"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:未轉接"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="TIME_DELAY">{2}</xliff:g> 秒後 <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 58607f02acc2..3f2d5473eb1a 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -57,7 +57,8 @@ <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"I-ID Yocingo Olungenayo"</string> - <string name="ClirMmi" msgid="4702929460236547156">"I-ID Yomshayeli Ephumayo"</string> + <!-- no translation found for ClirMmi (6752346475055446417) --> + <skip /> <string name="ColpMmi" msgid="4736462893284419302">"I-ID yomugqa exhumekile"</string> <string name="ColrMmi" msgid="5889782479745764278">"I-ID yomugqa oxhumekile ikhawulelwe"</string> <string name="CfMmi" msgid="8390012691099787178">"Ukudlulisa ikholi"</string> @@ -148,8 +149,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"I-Wi-Fi kuphela"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g>Ukwenza ikholi kwe-Cross Sim"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Akudlulisiwe"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> emuva kwamasekhondi angu-<xliff:g id="TIME_DELAY">{2}</xliff:g>"</string> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 0185714dc342..bb2665584600 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2486,7 +2486,8 @@ <attr name="singleUser" /> <attr name="directBootAware" /> <attr name="visibleToInstantApps" /> - <!-- The code for this component is located in the given split. --> + <!-- The code for this component is located in the given split. + @deprecated Do not use it. This is not supported. --> <attr name="splitName" /> </declare-styleable> @@ -2602,7 +2603,8 @@ must also be {@link android.R.attr#exported} if this flag is set. --> <attr name="externalService" format="boolean" /> <attr name="visibleToInstantApps" /> - <!-- The code for this component is located in the given split. --> + <!-- The code for this component is located in the given split. + @deprecated Do not use it. This is not supported. --> <attr name="splitName" /> <!-- If true, and this is an {@link android.R.attr#isolatedProcess} service, the service will be spawned from an Application Zygote, instead of the regular Zygote. diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 1845faacc891..272e130920f3 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -58,6 +58,7 @@ <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item> <item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item> <item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_no_calling</xliff:g></item> <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item> <item><xliff:g id="id">@string/status_bar_sensors_off</xliff:g></item> </string-array> @@ -94,6 +95,7 @@ <string translatable="false" name="status_bar_microphone">microphone</string> <string translatable="false" name="status_bar_camera">camera</string> <string translatable="false" name="status_bar_airplane">airplane</string> + <string translatable="false" name="status_bar_no_calling">no_calling</string> <string translatable="false" name="status_bar_sensors_off">sensors_off</string> <string translatable="false" name="status_bar_screen_record">screen_record</string> @@ -991,6 +993,11 @@ <!-- Time to wait while a button is pressed before triggering a very long press. --> <integer name="config_veryLongPressTimeout">3500</integer> + <!-- Time to wait before sending a HOME intent when waking up from power/home button. + (0 - do not send HOME intent on wakeup) + --> + <integer name="config_wakeUpToLastStateTimeoutMillis">0</integer> + <!-- Package name for default keyguard appwidget [DO NOT TRANSLATE] --> <string name="widget_default_package_name" translatable="false"></string> @@ -4610,4 +4617,9 @@ If omitted, image editing will not be offered via Chooser. This name is in the ComponentName flattened format (package/class) [DO NOT TRANSLATE] --> <string name="config_systemImageEditor" translatable="false"></string> + + <!-- Whether to force WindowOrientationListener to keep listening to its sensor, even when + dreaming. This allows the AoD to rotate on devices without a wake device_orientation + sensor. Note that this flag should only be enabled for development/testing use. --> + <bool name="config_forceOrientationListenerEnabledWhileDreaming">false</bool> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c72a0cd63b36..0c86905efbce 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -438,6 +438,7 @@ <java-symbol type="integer" name="config_veryLongPressTimeout" /> <java-symbol type="integer" name="config_longPressOnBackBehavior" /> <java-symbol type="bool" name="config_allowStartActivityForLongPressOnPowerInSetup" /> + <java-symbol type="integer" name="config_wakeUpToLastStateTimeoutMillis" /> <java-symbol type="integer" name="config_lowMemoryKillerMinFreeKbytesAdjust" /> <java-symbol type="integer" name="config_lowMemoryKillerMinFreeKbytesAbsolute" /> <java-symbol type="integer" name="config_max_pan_devices" /> @@ -2947,6 +2948,7 @@ <java-symbol type="string" name="status_bar_secure" /> <java-symbol type="string" name="status_bar_clock" /> <java-symbol type="string" name="status_bar_airplane" /> + <java-symbol type="string" name="status_bar_no_calling" /> <java-symbol type="string" name="status_bar_mobile" /> <java-symbol type="string" name="status_bar_ethernet" /> <java-symbol type="string" name="status_bar_vpn" /> @@ -4146,4 +4148,6 @@ <java-symbol type="bool" name="config_attachNavBarToAppDuringTransition" /> <java-symbol type="bool" name="config_enableBackSound" /> + + <java-symbol type="bool" name="config_forceOrientationListenerEnabledWhileDreaming" /> </resources> diff --git a/core/tests/coretests/src/android/content/OWNERS b/core/tests/coretests/src/android/content/OWNERS index 696aa11618ff..912db1e835dc 100644 --- a/core/tests/coretests/src/android/content/OWNERS +++ b/core/tests/coretests/src/android/content/OWNERS @@ -1,4 +1,3 @@ per-file ContextTest.java = file:/services/core/java/com/android/server/wm/OWNERS per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS -per-file AppSearchPersonTest.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS diff --git a/core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java b/core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java new file mode 100644 index 000000000000..1ff88f70019e --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.Person; + +import org.junit.Test; + +public class AppSearchPersonTest { + + @Test + public void testBuildPersonAndGetValue() { + final String name = "name"; + final String key = "key"; + final String uri = "name:name"; + + final Person person = new AppSearchPerson.Builder(uri) + .setName(name) + .setKey(key) + .setIsBot(true) + .setIsImportant(false) + .build() + .toPerson(); + + assertThat(person.getName()).isEqualTo(name); + assertThat(person.getKey()).isEqualTo(key); + assertThat(person.getUri()).isEqualTo(uri); + assertThat(person.isBot()).isTrue(); + assertThat(person.isImportant()).isFalse(); + } +} diff --git a/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java new file mode 100644 index 000000000000..da92e69b6378 --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.Person; +import android.content.ComponentName; +import android.content.Intent; +import android.util.ArraySet; + +import org.junit.Test; + +import java.util.Set; + +public class AppSearchShortcutInfoTest { + + @Test + public void testBuildShortcutAndGetValue() { + final String category = + "android.app.stubs.SHARE_SHORTCUT_CATEGORY"; + final String id = "shareShortcut"; + final String shortcutIconResName = "shortcut"; + final ComponentName activity = new ComponentName("xxx", "s"); + final Person person = new Person.Builder() + .setBot(false) + .setName("BubbleBot") + .setImportant(true) + .build(); + + final Set<String> categorySet = new ArraySet<>(); + categorySet.add(category); + final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW); + final ShortcutInfo shortcut = new AppSearchShortcutInfo.Builder(id) + .setActivity(activity) + .setText(id) + .setIconResName(shortcutIconResName) + .setIntent(shortcutIntent) + .setPerson(person) + .setCategories(categorySet) + .setFlags(ShortcutInfo.FLAG_LONG_LIVED) + .build() + .toShortcutInfo(); + + assertThat(shortcut.getId()).isEqualTo(id); + assertThat(shortcut.getShortLabel()).isEqualTo(id); + assertThat(shortcut.getIconResName()).isEqualTo(shortcutIconResName); + assertThat(shortcut.getIntent().toString()).isEqualTo(shortcut.toString()); + assertThat(shortcut.getPersons().length).isEqualTo(1); + assertThat(shortcut.getPersons()[0]).isEqualTo(person); + assertThat(shortcut.getCategories()).isEqualTo(categorySet); + assertThat(shortcut.getFlags()).isEqualTo(ShortcutInfo.FLAG_LONG_LIVED); + assertThat(shortcut.getActivity()).isEqualTo(activity); + } +} diff --git a/core/tests/coretests/src/android/content/pm/OWNERS b/core/tests/coretests/src/android/content/pm/OWNERS new file mode 100644 index 000000000000..711f5f012b8b --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/OWNERS @@ -0,0 +1,2 @@ +per-file AppSearchPersonTest.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS + diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java index 05ff21853131..65ea2a8373aa 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java @@ -21,6 +21,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.content.res.AssetManager; import android.graphics.fonts.FontCustomizationParser; @@ -44,11 +46,12 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardCopyOption; +import java.util.HashMap; import java.util.Locale; +import java.util.Map; @SmallTest @RunWith(AndroidJUnit4.class) @@ -137,23 +140,55 @@ public class TypefaceSystemFallbackTest { } } - private static void buildSystemFallback(String xml, - FontCustomizationParser.Result oemCustomization, ArrayMap<String, Typeface> fontMap, - ArrayMap<String, FontFamily[]> fallbackMap) { + private static void buildSystemFallback( + @NonNull String xml, + @Nullable String oemXml, + @NonNull ArrayMap<String, Typeface> outFontMap, + @NonNull ArrayMap<String, FontFamily[]> outFallbackMap) { try (FileOutputStream fos = new FileOutputStream(TEST_FONTS_XML)) { - fos.write(xml.getBytes(Charset.forName("UTF-8"))); + fos.write(xml.getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { throw new RuntimeException(e); } - final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(TEST_FONTS_XML, - TEST_FONT_DIR, TEST_UPDATABLE_FONT_DIR, oemCustomization, fallbackMap); - Typeface.initSystemDefaultTypefaces(fontMap, fallbackMap, aliases); + String oemXmlPath; + if (oemXml != null) { + try (FileOutputStream fos = new FileOutputStream(TEST_OEM_XML)) { + fos.write(oemXml.getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new RuntimeException(e); + } + oemXmlPath = TEST_OEM_XML; + } else { + oemXmlPath = null; + } + + Map<String, File> updatableFontMap = new HashMap<>(); + for (File file : new File(TEST_UPDATABLE_FONT_DIR).listFiles()) { + updatableFontMap.put(file.getName(), file); + } + + FontConfig fontConfig; + try { + fontConfig = FontListParser.parse( + TEST_FONTS_XML, TEST_FONT_DIR, oemXmlPath, TEST_OEM_DIR, updatableFontMap); + } catch (IOException | XmlPullParserException e) { + throw new RuntimeException(e); + } + + Map<String, FontFamily[]> fallbackMap = SystemFonts.buildSystemFallback(fontConfig); + Map<String, Typeface> typefaceMap = SystemFonts.buildSystemTypefaces( + fontConfig, fallbackMap); + + outFontMap.clear(); + outFontMap.putAll(typefaceMap); + outFallbackMap.clear(); + outFallbackMap.putAll(fallbackMap); } private static FontCustomizationParser.Result readFontCustomization(String oemXml) { try (InputStream is = new ByteArrayInputStream(oemXml.getBytes(StandardCharsets.UTF_8))) { - return FontCustomizationParser.parse(is, TEST_OEM_DIR); + return FontCustomizationParser.parse(is, TEST_OEM_DIR, null); } catch (IOException | XmlPullParserException e) { throw new RuntimeException(e); } @@ -161,19 +196,22 @@ public class TypefaceSystemFallbackTest { @Test public void testBuildSystemFallback() { - final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); - final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - - final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(SYSTEM_FONTS_XML, - SYSTEM_FONT_DIR, oemCustomization, fallbackMap); + FontConfig fontConfig; + try { + fontConfig = FontListParser.parse( + SYSTEM_FONTS_XML, SYSTEM_FONT_DIR, null, TEST_OEM_DIR, null); + } catch (IOException | XmlPullParserException e) { + throw new RuntimeException(e); + } + assertFalse(fontConfig.getAliases().isEmpty()); + assertFalse(fontConfig.getFontFamilies().isEmpty()); - assertNotNull(aliases); + Map<String, FontFamily[]> fallbackMap = SystemFonts.buildSystemFallback(fontConfig); assertFalse(fallbackMap.isEmpty()); - Typeface.initSystemDefaultTypefaces(fontMap, fallbackMap, aliases); - assertFalse(fontMap.isEmpty()); + Map<String, Typeface> typefaceMap = SystemFonts.buildSystemTypefaces( + fontConfig, fallbackMap); + assertFalse(typefaceMap.isEmpty()); } @Test @@ -193,10 +231,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); assertEquals(1, fontMap.size()); assertTrue(fontMap.containsKey("sans-serif")); @@ -223,10 +259,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -271,10 +305,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -318,10 +350,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -370,10 +400,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -418,10 +446,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -459,10 +485,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -500,10 +524,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -550,10 +572,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); paint.setTypeface(fontMap.get("sans-serif")); @@ -594,10 +614,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -635,10 +653,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -673,10 +689,8 @@ public class TypefaceSystemFallbackTest { + "</fonts-modification>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - readFontCustomization(oemXml); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, oemXml, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -711,10 +725,8 @@ public class TypefaceSystemFallbackTest { + "</fonts-modification>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - readFontCustomization(oemXml); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, oemXml, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -745,10 +757,8 @@ public class TypefaceSystemFallbackTest { + "</fonts-modification>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - readFontCustomization(oemXml); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, oemXml, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -798,10 +808,8 @@ public class TypefaceSystemFallbackTest { + "</fonts-modification>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - readFontCustomization(oemXml); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, oemXml, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -856,12 +864,10 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); // Install all2em.ttf as a3em.ttf copyAssetToFile("fonts/all2em.ttf", new File(TEST_UPDATABLE_FONT_DIR, "a3em.ttf")); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); diff --git a/core/tests/coretests/src/android/graphics/TypefaceTest.java b/core/tests/coretests/src/android/graphics/TypefaceTest.java index 392c6b7199a5..d12f495055e1 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceTest.java @@ -27,7 +27,6 @@ import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; import android.os.SharedMemory; import android.text.FontConfig; -import android.util.Pair; import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; @@ -41,7 +40,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.nio.ByteOrder; -import java.util.HashMap; import java.util.Map; import java.util.Random; @@ -197,10 +195,10 @@ public class TypefaceTest { @SmallTest @Test public void testSerialize() throws Exception { - HashMap<String, Typeface> systemFontMap = new HashMap<>(); - Pair<FontConfig.Alias[], Map<String, FontFamily[]>> res = - SystemFonts.initializePreinstalledFonts(); - Typeface.initSystemDefaultTypefaces(systemFontMap, res.second, res.first); + FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig(); + Map<String, FontFamily[]> fallbackMap = SystemFonts.buildSystemFallback(fontConfig); + Map<String, Typeface> systemFontMap = SystemFonts.buildSystemTypefaces(fontConfig, + fallbackMap); SharedMemory sharedMemory = Typeface.serializeFontMap(systemFontMap); Map<String, Typeface> copiedFontMap = Typeface.deserializeFontMap(sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN)); diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/coretests/src/android/os/VibrationEffectTest.java index c357414c8913..1d56e179317f 100644 --- a/core/tests/coretests/src/android/os/VibrationEffectTest.java +++ b/core/tests/coretests/src/android/os/VibrationEffectTest.java @@ -39,6 +39,7 @@ import android.platform.test.annotations.Presubmit; import com.android.internal.R; +import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @@ -168,15 +169,30 @@ public class VibrationEffectTest { } @Test - public void testScalePrebaked_ignoresScaleAndReturnsSameEffect() { - VibrationEffect initial = VibrationEffect.get(VibrationEffect.RINGTONES[1]); - assertSame(initial, initial.scale(0.5f)); + public void testScalePrebaked_scalesFallbackEffect() { + VibrationEffect.Prebaked prebaked = + (VibrationEffect.Prebaked) VibrationEffect.get(VibrationEffect.RINGTONES[1]); + assertSame(prebaked, prebaked.scale(0.5f)); + + prebaked = new VibrationEffect.Prebaked(VibrationEffect.EFFECT_CLICK, + VibrationEffect.EFFECT_STRENGTH_MEDIUM, TEST_ONE_SHOT); + VibrationEffect.OneShot scaledFallback = + (VibrationEffect.OneShot) prebaked.scale(0.5f).getFallbackEffect(); + assertEquals(34, scaledFallback.getAmplitude(), AMPLITUDE_SCALE_TOLERANCE); } @Test - public void testResolvePrebaked_ignoresDefaultAmplitudeAndReturnsSameEffect() { - VibrationEffect initial = VibrationEffect.get(VibrationEffect.RINGTONES[1]); - assertSame(initial, initial.resolve(1000)); + public void testResolvePrebaked_resolvesFallbackEffectIfSet() { + VibrationEffect.Prebaked prebaked = + (VibrationEffect.Prebaked) VibrationEffect.get(VibrationEffect.RINGTONES[1]); + assertSame(prebaked, prebaked.resolve(1000)); + + prebaked = new VibrationEffect.Prebaked(VibrationEffect.EFFECT_CLICK, + VibrationEffect.EFFECT_STRENGTH_MEDIUM, + VibrationEffect.createOneShot(1, VibrationEffect.DEFAULT_AMPLITUDE)); + VibrationEffect.OneShot resolvedFallback = + (VibrationEffect.OneShot) prebaked.resolve(10).getFallbackEffect(); + assertEquals(10, resolvedFallback.getAmplitude()); } @Test @@ -352,6 +368,36 @@ public class VibrationEffectTest { INTENSITY_SCALE_TOLERANCE); } + @Test + public void getEffectStrength_returnsValueFromConstructor() { + VibrationEffect.Prebaked effect = new VibrationEffect.Prebaked(VibrationEffect.EFFECT_CLICK, + VibrationEffect.EFFECT_STRENGTH_LIGHT, null); + Assert.assertEquals(VibrationEffect.EFFECT_STRENGTH_LIGHT, effect.getEffectStrength()); + } + + @Test + public void getFallbackEffect_withFallbackDisabled_isNull() { + VibrationEffect fallback = VibrationEffect.createOneShot(100, 100); + VibrationEffect.Prebaked effect = new VibrationEffect.Prebaked(VibrationEffect.EFFECT_CLICK, + false, VibrationEffect.EFFECT_STRENGTH_LIGHT); + Assert.assertNull(effect.getFallbackEffect()); + } + + @Test + public void getFallbackEffect_withoutEffectSet_isNull() { + VibrationEffect.Prebaked effect = new VibrationEffect.Prebaked(VibrationEffect.EFFECT_CLICK, + true, VibrationEffect.EFFECT_STRENGTH_LIGHT); + Assert.assertNull(effect.getFallbackEffect()); + } + + @Test + public void getFallbackEffect_withFallback_returnsValueFromConstructor() { + VibrationEffect fallback = VibrationEffect.createOneShot(100, 100); + VibrationEffect.Prebaked effect = new VibrationEffect.Prebaked(VibrationEffect.EFFECT_CLICK, + VibrationEffect.EFFECT_STRENGTH_LIGHT, fallback); + Assert.assertEquals(fallback, effect.getFallbackEffect()); + } + private Resources mockRingtoneResources() { return mockRingtoneResources(new String[] { RINGTONE_URI_1, diff --git a/core/tests/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java index 64e6f82d82af..e301037d01f1 100644 --- a/core/tests/coretests/src/android/text/FontFallbackSetup.java +++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java @@ -19,14 +19,15 @@ package android.text; import android.annotation.NonNull; import android.content.Context; import android.content.res.AssetManager; +import android.graphics.FontListParser; import android.graphics.Typeface; -import android.graphics.fonts.FontCustomizationParser; import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; -import android.util.ArrayMap; import androidx.test.InstrumentationRegistry; +import org.xmlpull.v1.XmlPullParserException; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -34,12 +35,13 @@ import java.io.InputStream; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.StandardCopyOption; +import java.util.Map; public class FontFallbackSetup implements AutoCloseable { private final String[] mTestFontFiles; private final String mXml; private final String mTestFontsDir; - final ArrayMap<String, Typeface> mFontMap = new ArrayMap<>(); + private final Map<String, Typeface> mFontMap; public FontFallbackSetup(@NonNull String testSubDir, @NonNull String[] testFontFiles, @NonNull String xml) { @@ -75,12 +77,15 @@ public class FontFallbackSetup implements AutoCloseable { throw new RuntimeException(e); } - final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(testFontsXml, - mTestFontsDir, oemCustomization, fallbackMap); - Typeface.initSystemDefaultTypefaces(mFontMap, fallbackMap, aliases); + FontConfig fontConfig; + try { + fontConfig = FontListParser.parse(testFontsXml, mTestFontsDir, null, null, null); + } catch (IOException | XmlPullParserException e) { + throw new RuntimeException(e); + } + + Map<String, FontFamily[]> fallbackMap = SystemFonts.buildSystemFallback(fontConfig); + mFontMap = SystemFonts.buildSystemTypefaces(fontConfig, fallbackMap); } @NonNull diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index af13cc07ced1..2770ed820562 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -45,6 +45,7 @@ import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -681,6 +682,19 @@ public class InsetsControllerTest { } @Test + public void testNotifyCaptionInsetsOnlyChange() { + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + final InsetsState state = new InsetsState(mController.getState(), true); + reset(mTestHost); + mController.setCaptionInsetsHeight(100); + verify(mTestHost).notifyInsetsChanged(); + reset(mTestHost); + mController.setCaptionInsetsHeight(0); + verify(mTestHost).notifyInsetsChanged(); + }); + } + + @Test public void testRequestedState() { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { final InsetsState state = mTestHost.getRequestedState(); diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index f371a7ffce23..c67174f0ae1e 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -23,7 +23,7 @@ import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE; import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE; +import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; @@ -35,6 +35,7 @@ import static androidx.test.InstrumentationRegistry.getInstrumentation; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import android.content.Context; import android.os.Binder; @@ -50,6 +51,9 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + /** * Tests for {@link ViewRootImpl} * @@ -180,7 +184,7 @@ public class ViewRootImplTest { public void adjustLayoutParamsForCompatibility_noAdjustBehavior() { final WindowInsetsController controller = mViewRootImpl.getInsetsController(); final WindowManager.LayoutParams attrs = mViewRootImpl.mWindowAttributes; - final int behavior = BEHAVIOR_SHOW_BARS_BY_SWIPE; + final int behavior = BEHAVIOR_DEFAULT; controller.setSystemBarsBehavior(behavior); attrs.systemUiVisibility = SYSTEM_UI_FLAG_IMMERSIVE_STICKY; ViewRootImpl.adjustLayoutParamsForCompatibility(attrs); @@ -196,6 +200,26 @@ public class ViewRootImplTest { } /** + * Ensure scroll capture request handles a ViewRootImpl with no view tree. + */ + @Test + public void requestScrollCapture_withoutContentRoot() { + final CountDownLatch latch = new CountDownLatch(1); + mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() { + @Override + public void onUnavailable() { + latch.countDown(); + } + }); + try { + if (latch.await(100, TimeUnit.MILLISECONDS)) { + return; // pass + } + } catch (InterruptedException e) { /* ignore */ } + fail("requestScrollCapture did not respond"); + } + + /** * When window doesn't have focus, keys should be dropped. */ @Test diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java index 82788c806fed..5def552c72b4 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java @@ -16,14 +16,24 @@ package android.view.textclassifier; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import android.app.PendingIntent; +import android.app.RemoteAction; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.LocaleList; import android.os.Parcel; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -107,4 +117,46 @@ public class TextSelectionTest { assertEquals(1, resultSystemTcMetadata.getUserId()); assertFalse(resultSystemTcMetadata.useDefaultTextClassifier()); } + + @Test + public void testToBuilder() { + final Context context = InstrumentationRegistry.getInstrumentation().getContext(); + final int startIndex = 13; + final int endIndex = 37; + final String id = "id"; + final Icon icon1 = generateTestIcon(5, 5, Color.RED); + + final TextClassification classification = new TextClassification.Builder() + .addAction(new RemoteAction(icon1, "title1", "desc1", + PendingIntent.getActivity(context, 0, new Intent("action1"), 0))) + .setEntityType(TextClassifier.TYPE_ADDRESS, 1.0f) + .build(); + final TextSelection textSelection = new TextSelection.Builder(startIndex, endIndex) + .setId(id) + .setEntityType(TextClassifier.TYPE_ADDRESS, 1.0f) + .setExtras(BUNDLE) + .setTextClassification(classification) + .build(); + + final TextSelection fromBuilder = textSelection.toBuilder().build(); + + assertThat(fromBuilder.getId()).isEqualTo(textSelection.getId()); + assertThat(fromBuilder.getSelectionStartIndex()) + .isEqualTo(textSelection.getSelectionStartIndex()); + assertThat(fromBuilder.getSelectionEndIndex()) + .isEqualTo(textSelection.getSelectionEndIndex()); + assertThat(fromBuilder.getTextClassification()) + .isSameInstanceAs(textSelection.getTextClassification()); + assertThat(fromBuilder.getExtras()).isSameInstanceAs(textSelection.getExtras()); + } + + private Icon generateTestIcon(int width, int height, int colorValue) { + final int numPixels = width * height; + final int[] colors = new int[numPixels]; + for (int i = 0; i < numPixels; ++i) { + colors[i] = colorValue; + } + final Bitmap bitmap = Bitmap.createBitmap(colors, width, height, Bitmap.Config.ARGB_8888); + return Icon.createWithBitmap(bitmap); + } } diff --git a/core/tests/coretests/src/android/widget/AbsSeekBarTest.java b/core/tests/coretests/src/android/widget/AbsSeekBarTest.java index 5371a0f8d9d7..ccd873dc390e 100644 --- a/core/tests/coretests/src/android/widget/AbsSeekBarTest.java +++ b/core/tests/coretests/src/android/widget/AbsSeekBarTest.java @@ -30,7 +30,6 @@ import android.graphics.Rect; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.RectShape; import android.platform.test.annotations.Presubmit; -import android.view.View; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; @@ -48,6 +47,7 @@ import java.util.List; @Presubmit public class AbsSeekBarTest { + public static final int PADDING = 10; private Context mContext; private AbsSeekBar mBar; @@ -59,34 +59,42 @@ public class AbsSeekBarTest { @Test public void testExclusionForThumb_limitedTo48dp() { - mBar.setPadding(10, 10, 10, 10); + mBar.setPadding(PADDING, PADDING, PADDING, PADDING); mBar.setThumb(newThumb(dpToPxSize(20))); mBar.setMin(0); mBar.setMax(100); mBar.setProgress(50); + + final int thumbOffset = mBar.getThumbOffset(); + measureAndLayout(dpToPxSize(200), dpToPxSize(100)); List<Rect> exclusions = mBar.getSystemGestureExclusionRects(); assertEquals("exclusions should be size 1, but was " + exclusions, 1, exclusions.size()); assertEquals("exclusion should be centered on thumb", - center(mBar), center(exclusions.get(0))); + center(offset(mBar.getThumb().getBounds(), PADDING - thumbOffset, PADDING)), + center(exclusions.get(0))); assertEquals("exclusion should be 48dp high", dpToPxSize(48), exclusions.get(0).height()); assertEquals("exclusion should be 48dp wide", dpToPxSize(48), exclusions.get(0).width()); } @Test public void testExclusionForThumb_limitedToHeight() { - mBar.setPadding(10, 10, 10, 10); + mBar.setPadding(PADDING, PADDING, PADDING, PADDING); mBar.setThumb(newThumb(dpToPxSize(20))); mBar.setMin(0); mBar.setMax(100); mBar.setProgress(50); + + final int thumbOffset = mBar.getThumbOffset(); + measureAndLayout(dpToPxSize(200), dpToPxSize(32)); List<Rect> exclusions = mBar.getSystemGestureExclusionRects(); assertEquals("exclusions should be size 1, but was " + exclusions, 1, exclusions.size()); assertEquals("exclusion should be centered on thumb", - center(mBar), center(exclusions.get(0))); + center(offset(mBar.getThumb().getBounds(), PADDING - thumbOffset, PADDING)), + center(exclusions.get(0))); assertEquals("exclusion should be 32dp high", dpToPxSize(32), exclusions.get(0).height()); assertEquals("exclusion should be 32dp wide", dpToPxSize(32), exclusions.get(0).width()); } @@ -95,7 +103,7 @@ public class AbsSeekBarTest { public void testExclusionForThumb_passesThroughUserExclusions() { mBar.setSystemGestureExclusionRects(Arrays.asList(new Rect(1, 2, 3, 4))); - mBar.setPadding(10, 10, 10, 10); + mBar.setPadding(PADDING, PADDING, PADDING, PADDING); mBar.setThumb(newThumb(dpToPxSize(20))); mBar.setMin(0); mBar.setMax(100); @@ -110,12 +118,37 @@ public class AbsSeekBarTest { assertThat(mBar.getSystemGestureExclusionRects(), hasSize(2)); } + @Test + public void testGrowRectTo_evenInitialDifference() { + doGrowRectTest(new Rect(0, 0, 0, 0), 10, new Rect(-5, -5, 5, 5)); + } + + @Test + public void testGrowRectTo_unevenInitialDifference() { + doGrowRectTest(new Rect(0, 0, 1, 1), 10, new Rect(-5, -5, 5, 5)); + } + + @Test + public void testGrowRectTo_unevenInitialDifference_unevenSize() { + doGrowRectTest(new Rect(0, 0, 0, 0), 9, new Rect(-5, -5, 4, 4)); + } + + public void doGrowRectTest(Rect in, int minimumSize, Rect expected) { + Rect result = new Rect(in); + mBar.growRectTo(result, minimumSize); + + assertEquals("grown rect", expected, result); + assertEquals("grown rect center point", center(expected), center(result)); + } + private Point center(Rect rect) { return new Point(rect.centerX(), rect.centerY()); } - private Point center(View view) { - return center(new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom())); + private Rect offset(Rect rect, int dx, int dy) { + Rect result = new Rect(rect); + result.offset(dx, dy); + return result; } private ShapeDrawable newThumb(int size) { diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java index 2ede7517c306..c9c81ac51944 100644 --- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java @@ -34,6 +34,8 @@ import androidx.test.filters.SmallTest; import org.junit.Test; +import java.util.Arrays; + /** * Test class for {@link MeasuredEnergyStats}. * @@ -114,7 +116,7 @@ public class MeasuredEnergyStatsTest { } @Test - public void testReadWriteSummaryParcel() { + public void testCreateAndReadSummaryFromParcel() { final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; @@ -126,35 +128,21 @@ public class MeasuredEnergyStatsTest { stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); final Parcel parcel = Parcel.obtain(); - MeasuredEnergyStats.writeSummaryToParcel(stats, parcel); - - - final boolean[] newSupportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; - newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; - newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = true; // switched from false to true - newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = false; // switched true to false - MeasuredEnergyStats newStats = new MeasuredEnergyStats(newSupportedEnergyBuckets); + MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false); parcel.setDataPosition(0); - MeasuredEnergyStats.readSummaryFromParcel(newStats, parcel); + MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel); for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { - if (!newSupportedEnergyBuckets[i]) { - assertFalse(newStats.isEnergyBucketSupported(i)); - assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i)); - } else if (!supportedEnergyBuckets[i]) { - assertTrue(newStats.isEnergyBucketSupported(i)); - assertEquals(0L, newStats.getAccumulatedBucketEnergy(i)); - } else { - assertTrue(newStats.isEnergyBucketSupported(i)); - assertEquals(stats.getAccumulatedBucketEnergy(i), - newStats.getAccumulatedBucketEnergy(i)); - } + assertEquals(stats.isEnergyBucketSupported(i), + newStats.isEnergyBucketSupported(i)); + assertEquals(stats.getAccumulatedBucketEnergy(i), + newStats.getAccumulatedBucketEnergy(i)); } parcel.recycle(); } @Test - public void testCreateAndReadSummaryFromParcel() { + public void testCreateAndReadSummaryFromParcel_existingTemplate() { final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; @@ -171,7 +159,7 @@ public class MeasuredEnergyStatsTest { stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 63, true); final Parcel parcel = Parcel.obtain(); - MeasuredEnergyStats.writeSummaryToParcel(stats, parcel); + MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false); final boolean[] newSupportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; @@ -200,6 +188,55 @@ public class MeasuredEnergyStatsTest { } @Test + public void testCreateAndReadSummaryFromParcel_skipZero() { + final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; + Arrays.fill(supportedEnergyBuckets, true); + + final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets); + // Accumulate energy in one bucket, the rest should be zero + stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 200, true); + + final Parcel includeZerosParcel = Parcel.obtain(); + MeasuredEnergyStats.writeSummaryToParcel(stats, includeZerosParcel, false); + includeZerosParcel.setDataPosition(0); + + MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel( + includeZerosParcel); + + for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + if (i == ENERGY_BUCKET_SCREEN_ON) { + assertEquals(stats.isEnergyBucketSupported(i), + newStats.isEnergyBucketSupported(i)); + assertEquals(stats.getAccumulatedBucketEnergy(i), + newStats.getAccumulatedBucketEnergy(i)); + } else { + assertTrue(newStats.isEnergyBucketSupported(i)); + assertEquals(0L, newStats.getAccumulatedBucketEnergy(i)); + } + } + includeZerosParcel.recycle(); + + final Parcel skipZerosParcel = Parcel.obtain(); + MeasuredEnergyStats.writeSummaryToParcel(stats, skipZerosParcel, true); + skipZerosParcel.setDataPosition(0); + + newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(skipZerosParcel); + + for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + if (i == ENERGY_BUCKET_SCREEN_ON) { + assertEquals(stats.isEnergyBucketSupported(i), + newStats.isEnergyBucketSupported(i)); + assertEquals(stats.getAccumulatedBucketEnergy(i), + newStats.getAccumulatedBucketEnergy(i)); + } else { + assertFalse(newStats.isEnergyBucketSupported(i)); + assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i)); + } + } + skipZerosParcel.recycle(); + } + + @Test public void testUpdateBucket() { final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; diff --git a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java index 7eca320d4aeb..272f2287dd6e 100644 --- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java +++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java @@ -16,6 +16,8 @@ package com.android.internal.statusbar; +import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; + import static com.google.common.truth.Truth.assertThat; import android.os.Binder; @@ -56,8 +58,8 @@ public class RegisterStatusBarResultTest { 0x20 /* disabledFlags2 */, new Binder() /* imeToken */, true /* navbarColorManagedByIme */, + BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE, true /* appFullscreen */, - true /* appImmersive */, new int[0] /* transientBarTypes */); final RegisterStatusBarResult copy = clone(original); @@ -76,8 +78,8 @@ public class RegisterStatusBarResultTest { assertThat(copy.mDisabledFlags2).isEqualTo(original.mDisabledFlags2); assertThat(copy.mImeToken).isSameInstanceAs(original.mImeToken); assertThat(copy.mNavbarColorManagedByIme).isEqualTo(original.mNavbarColorManagedByIme); + assertThat(copy.mBehavior).isEqualTo(original.mBehavior); assertThat(copy.mAppFullscreen).isEqualTo(original.mAppFullscreen); - assertThat(copy.mAppImmersive).isEqualTo(original.mAppImmersive); assertThat(copy.mTransientBarTypes).isEqualTo(original.mTransientBarTypes); } diff --git a/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java b/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java index 4c3eaeb1730b..7175f562d7ef 100644 --- a/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java @@ -15,6 +15,8 @@ */ package com.android.internal.util; +import static android.Manifest.permission.NETWORK_SETTINGS; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -82,6 +84,7 @@ public class LocationPermissionCheckerTest { private int mAllowCoarseLocationApps; private int mFineLocationPermission; private int mAllowFineLocationApps; + private int mNetworkSettingsPermission; private int mCurrentUser; private boolean mIsLocationEnabled; private boolean mThrowSecurityException; @@ -138,6 +141,7 @@ public class LocationPermissionCheckerTest { mFineLocationPermission = PackageManager.PERMISSION_DENIED; mAllowCoarseLocationApps = AppOpsManager.MODE_ERRORED; mAllowFineLocationApps = AppOpsManager.MODE_ERRORED; + mNetworkSettingsPermission = PackageManager.PERMISSION_DENIED; } private void setupMockInterface() { @@ -151,6 +155,8 @@ public class LocationPermissionCheckerTest { .thenReturn(mCoarseLocationPermission); when(mMockContext.checkPermission(mManifestStringFine, -1, mUid)) .thenReturn(mFineLocationPermission); + when(mMockContext.checkPermission(NETWORK_SETTINGS, -1, mUid)) + .thenReturn(mNetworkSettingsPermission); when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(mIsLocationEnabled); } @@ -264,6 +270,21 @@ public class LocationPermissionCheckerTest { assertEquals(LocationPermissionChecker.ERROR_LOCATION_MODE_OFF, result); } + @Test + public void testenforceCanAccessScanResults_LocationModeDisabledHasNetworkSettings() + throws Exception { + mThrowSecurityException = false; + mIsLocationEnabled = false; + mNetworkSettingsPermission = PackageManager.PERMISSION_GRANTED; + setupTestCase(); + + final int result = + mChecker.checkLocationPermissionWithDetailInfo( + TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null); + assertEquals(LocationPermissionChecker.SUCCEEDED, result); + } + + private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) { try { r.run(); diff --git a/data/etc/Android.bp b/data/etc/Android.bp index fb8b17c1f159..201f649cde52 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -102,6 +102,14 @@ prebuilt_etc { } prebuilt_etc { + name: "privapp_whitelist_com.android.imsserviceentitlement", + product_specific: true, + sub_dir: "permissions", + src: "com.android.imsserviceentitlement.xml", + filename_from_src: true, +} + +prebuilt_etc { name: "privapp_whitelist_com.android.launcher3", system_ext_specific: true, sub_dir: "permissions", diff --git a/data/etc/car/com.android.car.shell.xml b/data/etc/car/com.android.car.shell.xml index 32666c8d9f68..6132d53b4651 100644 --- a/data/etc/car/com.android.car.shell.xml +++ b/data/etc/car/com.android.car.shell.xml @@ -15,7 +15,9 @@ ~ limitations under the License --> <permissions> - <privapp-permissions package="com.android.car.shell"> + <!-- CarShell now overrides the shell package and adding permission here + is ok. --> + <privapp-permissions package="com.android.shell"> <permission name="android.permission.INSTALL_PACKAGES" /> <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> </privapp-permissions> diff --git a/data/etc/com.android.imsserviceentitlement.xml b/data/etc/com.android.imsserviceentitlement.xml new file mode 100644 index 000000000000..4fd91c3ea58e --- /dev/null +++ b/data/etc/com.android.imsserviceentitlement.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.imsserviceentitlement"> + <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + </privapp-permissions> +</permissions> diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java index 73fff7207c45..ff381176e38d 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -16,10 +16,14 @@ package android.graphics; +import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.graphics.fonts.FontCustomizationParser; +import android.graphics.fonts.FontStyle; import android.graphics.fonts.FontVariationAxis; import android.os.Build; +import android.os.LocaleList; import android.text.FontConfig; import android.util.Xml; @@ -27,15 +31,16 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.regex.Pattern; /** * Parser for font config files. - * * @hide */ public class FontListParser { @@ -43,59 +48,102 @@ public class FontListParser { /* Parse fallback list (no names) */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException { - return parse(in, "/system/fonts", null); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(in, null); + parser.nextTag(); + return readFamilies(parser, "/system/fonts/", new FontCustomizationParser.Result(), null); } /** - * Parse the fonts.xml + * Parses system font config XMLs + * + * @param fontsXmlPath location of fonts.xml + * @param systemFontDir location of system font directory + * @param oemCustomizationXmlPath location of oem_customization.xml + * @param productFontDir location of oem customized font directory + * @param updatableFontMap map of updated font files. + * @return font configuration + * @throws IOException + * @throws XmlPullParserException */ - public static FontConfig parse(InputStream in, String fontDir, - @Nullable String updatableFontDir) throws XmlPullParserException, IOException { - try { + public static FontConfig parse( + @NonNull String fontsXmlPath, + @NonNull String systemFontDir, + @Nullable String oemCustomizationXmlPath, + @Nullable String productFontDir, + @Nullable Map<String, File> updatableFontMap + ) throws IOException, XmlPullParserException { + FontCustomizationParser.Result oemCustomization; + if (oemCustomizationXmlPath != null) { + try (InputStream is = new FileInputStream(oemCustomizationXmlPath)) { + oemCustomization = FontCustomizationParser.parse(is, productFontDir, + updatableFontMap); + } catch (IOException e) { + // OEM customization may not exists. Ignoring + oemCustomization = new FontCustomizationParser.Result(); + } + } else { + oemCustomization = new FontCustomizationParser.Result(); + } + + try (InputStream is = new FileInputStream(fontsXmlPath)) { XmlPullParser parser = Xml.newPullParser(); - parser.setInput(in, null); + parser.setInput(is, null); parser.nextTag(); - return readFamilies(parser, fontDir, updatableFontDir); - } finally { - in.close(); + return readFamilies(parser, systemFontDir, oemCustomization, updatableFontMap); } } - private static FontConfig readFamilies(XmlPullParser parser, String fontDir, - @Nullable String updatableFontDir) throws XmlPullParserException, IOException { + private static FontConfig readFamilies( + @NonNull XmlPullParser parser, + @NonNull String fontDir, + @NonNull FontCustomizationParser.Result customization, + @Nullable Map<String, File> updatableFontMap) + throws XmlPullParserException, IOException { List<FontConfig.Family> families = new ArrayList<>(); - List<FontConfig.Alias> aliases = new ArrayList<>(); + List<FontConfig.Alias> aliases = new ArrayList<>(customization.getAdditionalAliases()); + + Map<String, FontConfig.Family> oemNamedFamilies = + customization.getAdditionalNamedFamilies(); parser.require(XmlPullParser.START_TAG, null, "familyset"); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) continue; String tag = parser.getName(); if (tag.equals("family")) { - families.add(readFamily(parser, fontDir, updatableFontDir)); + FontConfig.Family family = readFamily(parser, fontDir, updatableFontMap); + String name = family.getFallbackName(); + if (name == null || !oemNamedFamilies.containsKey(name)) { + // The OEM customization overrides system named family. Skip if OEM + // customization XML defines the same named family. + families.add(family); + } } else if (tag.equals("alias")) { aliases.add(readAlias(parser)); } else { skip(parser); } } - return new FontConfig(families.toArray(new FontConfig.Family[families.size()]), - aliases.toArray(new FontConfig.Alias[aliases.size()])); + + families.addAll(oemNamedFamilies.values()); + return new FontConfig(families, aliases); } /** - * Reads a family element + * Read family tag in fonts.xml or oem_customization.xml */ public static FontConfig.Family readFamily(XmlPullParser parser, String fontDir, - @Nullable String updatableFontDir) throws XmlPullParserException, IOException { + @Nullable Map<String, File> updatableFontMap) + throws XmlPullParserException, IOException { final String name = parser.getAttributeValue(null, "name"); final String lang = parser.getAttributeValue("", "lang"); final String variant = parser.getAttributeValue(null, "variant"); - final List<FontConfig.Font> fonts = new ArrayList<FontConfig.Font>(); + final List<FontConfig.Font> fonts = new ArrayList<>(); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) continue; final String tag = parser.getName(); if (tag.equals("font")) { - fonts.add(readFont(parser, fontDir, updatableFontDir)); + fonts.add(readFont(parser, fontDir, updatableFontMap)); } else { skip(parser); } @@ -108,19 +156,22 @@ public class FontListParser { intVariant = FontConfig.Family.VARIANT_ELEGANT; } } - return new FontConfig.Family(name, fonts.toArray(new FontConfig.Font[fonts.size()]), lang, - intVariant); + return new FontConfig.Family(fonts, name, LocaleList.forLanguageTags(lang), intVariant); } /** Matches leading and trailing XML whitespace. */ private static final Pattern FILENAME_WHITESPACE_PATTERN = Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$"); - private static FontConfig.Font readFont(XmlPullParser parser, String fontDir, - @Nullable String updatableFontDir) throws XmlPullParserException, IOException { + private static FontConfig.Font readFont( + @NonNull XmlPullParser parser, + @NonNull String fontDir, + @Nullable Map<String, File> updatableFontMap) + throws XmlPullParserException, IOException { + String indexStr = parser.getAttributeValue(null, "index"); int index = indexStr == null ? 0 : Integer.parseInt(indexStr); - List<FontVariationAxis> axes = new ArrayList<FontVariationAxis>(); + List<FontVariationAxis> axes = new ArrayList<>(); String weightStr = parser.getAttributeValue(null, "weight"); int weight = weightStr == null ? 400 : Integer.parseInt(weightStr); boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style")); @@ -139,20 +190,45 @@ public class FontListParser { } } String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll(""); - String fontName = findFontFile(sanitizedName, fontDir, updatableFontDir); - return new FontConfig.Font(fontName, index, axes.toArray( - new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor); + String updatedName = findUpdatedFontFile(sanitizedName, updatableFontMap); + String filePath; + String originalPath; + if (updatedName != null) { + filePath = updatedName; + originalPath = fontDir + sanitizedName; + } else { + filePath = fontDir + sanitizedName; + originalPath = null; + } + + String varSettings; + if (axes.isEmpty()) { + varSettings = ""; + } else { + varSettings = FontVariationAxis.toFontVariationSettings( + axes.toArray(new FontVariationAxis[0])); + } + + return new FontConfig.Font(new File(filePath), + originalPath == null ? null : new File(originalPath), + new FontStyle( + weight, + isItalic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT + ), + index, + varSettings, + fallbackFor); } - private static String findFontFile(String fileName, String fontDir, - @Nullable String updatableFontDir) { - if (updatableFontDir != null) { - String updatableFontName = updatableFontDir + fileName; - if (new File(updatableFontName).exists()) { - return updatableFontName; + private static String findUpdatedFontFile(String name, + @Nullable Map<String, File> updatableFontMap) { + if (updatableFontMap != null) { + File updatedFile = updatableFontMap.get(name); + if (updatedFile != null) { + return updatedFile.getAbsolutePath(); } } - return fontDir + fileName; + return null; } private static FontVariationAxis readAxis(XmlPullParser parser) @@ -188,12 +264,12 @@ public class FontListParser { int depth = 1; while (depth > 0) { switch (parser.next()) { - case XmlPullParser.START_TAG: - depth++; - break; - case XmlPullParser.END_TAG: - depth--; - break; + case XmlPullParser.START_TAG: + depth++; + break; + case XmlPullParser.END_TAG: + depth--; + break; } } } diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java index 61a47493c2ff..cf2f970e3d58 100644 --- a/graphics/java/android/graphics/Point.java +++ b/graphics/java/android/graphics/Point.java @@ -20,8 +20,6 @@ import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; -import java.io.PrintWriter; - /** * Point holds two integer coordinates */ @@ -72,17 +70,6 @@ public class Point implements Parcelable { return this.x == x && this.y == y; } - /** - * Dumps a human-readable shortened string of the point into the given - * stream - * - * @param pw The {@link PrintWriter} into which the string representation of - * the point will be written. - */ - public final void dump(@NonNull PrintWriter pw) { - pw.print("["); pw.print(x); pw.print(","); pw.print(y); pw.print("]"); - } - @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java index 2d626b9a622f..8dd7f317c842 100644 --- a/graphics/java/android/graphics/RecordingCanvas.java +++ b/graphics/java/android/graphics/RecordingCanvas.java @@ -220,7 +220,7 @@ public final class RecordingCanvas extends BaseRecordingCanvas { CanvasProperty<Float> progress, RuntimeShader shader) { nDrawRipple(mNativeCanvasWrapper, cx.getNativeContainer(), cy.getNativeContainer(), radius.getNativeContainer(), paint.getNativeContainer(), - progress.getNativeContainer(), shader.getNativeShaderFactory()); + progress.getNativeContainer(), shader.getNativeShaderBuilder()); } /** diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java index 7f2e503ac8fd..1ace32277f0a 100644 --- a/graphics/java/android/graphics/RuntimeShader.java +++ b/graphics/java/android/graphics/RuntimeShader.java @@ -17,7 +17,6 @@ package android.graphics; import android.annotation.NonNull; -import android.annotation.Nullable; import libcore.util.NativeAllocationRegistry; @@ -33,14 +32,12 @@ public class RuntimeShader extends Shader { RuntimeShader.class.getClassLoader(), nativeGetFinalizer()); } - private byte[] mUniforms; - private Shader[] mInputShaders; private boolean mIsOpaque; /** - * Current native shader factory instance. + * Current native shader builder instance. */ - private long mNativeInstanceRuntimeShaderFactory; + private long mNativeInstanceRuntimeShaderBuilder; /** * Creates a new RuntimeShader. @@ -50,80 +47,86 @@ public class RuntimeShader extends Shader { * on number of uniforms declared by sksl. * @param isOpaque True if all pixels have alpha 1.0f. */ - public RuntimeShader(@NonNull String sksl, @Nullable byte[] uniforms, boolean isOpaque) { - this(sksl, uniforms, null, isOpaque, ColorSpace.get(ColorSpace.Named.SRGB)); + public RuntimeShader(@NonNull String sksl, boolean isOpaque) { + super(ColorSpace.get(ColorSpace.Named.SRGB)); + mIsOpaque = isOpaque; + mNativeInstanceRuntimeShaderBuilder = nativeCreateBuilder(sksl); + NoImagePreloadHolder.sRegistry.registerNativeAllocation( + this, mNativeInstanceRuntimeShaderBuilder); } /** - * Creates a new RuntimeShader. + * Sets the uniform value corresponding to this shader. If the shader does not have a uniform + * with that name or if the uniform is declared with a type other than float then an + * IllegalArgumentException is thrown. * - * @param sksl The text of SKSL program to run on the GPU. - * @param uniforms Array of parameters passed by the SKSL shader. Array size depends - * on number of uniforms declared by sksl. - * @param shaderInputs Array of shaders passed to the SKSL shader. Array size depends - * on the number of input shaders declared in the sksl - * @param isOpaque True if all pixels have alpha 1.0f. + * @param uniformName name matching the uniform declared in the SKSL shader + * @param value */ - public RuntimeShader(@NonNull String sksl, @Nullable byte[] uniforms, - @Nullable Shader[] shaderInputs, boolean isOpaque) { - this(sksl, uniforms, shaderInputs, isOpaque, ColorSpace.get(ColorSpace.Named.SRGB)); + public void setUniform(@NonNull String uniformName, float value) { + setUniform(uniformName, new float[] {value}); } - private RuntimeShader(@NonNull String sksl, @Nullable byte[] uniforms, - @Nullable Shader[] shaderInputs, boolean isOpaque, - ColorSpace colorSpace) { - super(colorSpace); - mUniforms = uniforms; - mInputShaders = shaderInputs; - mIsOpaque = isOpaque; - mNativeInstanceRuntimeShaderFactory = nativeCreateShaderFactory(sksl); - NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, - mNativeInstanceRuntimeShaderFactory); + /** + * Sets the uniform value corresponding to this shader. If the shader does not have a uniform + * with that name or if the uniform is declared with a type other than float2/vec2 then an + * IllegalArgumentException is thrown. + * + * @param uniformName name matching the uniform declared in the SKSL shader + * @param value1 + * @param value2 + */ + public void setUniform(@NonNull String uniformName, float value1, float value2) { + setUniform(uniformName, new float[] {value1, value2}); } /** - * Sets new value for shader parameters. + * Sets the uniform value corresponding to this shader. If the shader does not have a uniform + * with that name or if the uniform is declared with a type other than a vecN/floatN where N is + * the size of the values array then an IllegalArgumentException is thrown. * - * @param uniforms Array of parameters passed by the SKSL shader. Array size depends - * on number of uniforms declared by mSksl. + * @param uniformName name matching the uniform declared in the SKSL shader + * @param values */ - public void updateUniforms(@Nullable byte[] uniforms) { - mUniforms = uniforms; + public void setUniform(@NonNull String uniformName, float[] values) { + nativeUpdateUniforms(mNativeInstanceRuntimeShaderBuilder, uniformName, values); discardNativeInstance(); } /** - * Sets new values for the shaders that serve as inputs to this shader. + * Sets the uniform shader that is declares as input to this shader. If the shader does not + * have a uniform shader with that name then an IllegalArgumentException is thrown. * - * @param shaderInputs Array of Shaders passed into the SKSL shader. Array size depends - * on number of input shaders declared by sksl. + * @param shaderName name matching the uniform declared in the SKSL shader + * @param shader shader passed into the SKSL shader for sampling */ - public void updateInputShaders(@Nullable Shader[] shaderInputs) { - mInputShaders = shaderInputs; + public void setInputShader(@NonNull String shaderName, @NonNull Shader shader) { + nativeUpdateShader( + mNativeInstanceRuntimeShaderBuilder, shaderName, shader.getNativeInstance()); discardNativeInstance(); } /** @hide */ @Override protected long createNativeInstance(long nativeMatrix, boolean filterFromPaint) { - long[] nativeShaders = mInputShaders.length > 0 ? new long[mInputShaders.length] : null; - for (int i = 0; i < mInputShaders.length; i++) { - nativeShaders[i] = mInputShaders[i].getNativeInstance(filterFromPaint); - } - - return nativeCreate(mNativeInstanceRuntimeShaderFactory, nativeMatrix, mUniforms, - nativeShaders, colorSpace().getNativeInstance(), mIsOpaque); + return nativeCreateShader(mNativeInstanceRuntimeShaderBuilder, nativeMatrix, mIsOpaque); } - public long getNativeShaderFactory() { - return mNativeInstanceRuntimeShaderFactory; + public long getNativeShaderBuilder() { + return mNativeInstanceRuntimeShaderBuilder; } - private static native long nativeCreate(long shaderFactory, long matrix, byte[] inputs, - long[] shaderInputs, long colorSpaceHandle, boolean isOpaque); - - private static native long nativeCreateShaderFactory(String sksl); + public boolean isOpaque() { + return mIsOpaque; + } private static native long nativeGetFinalizer(); + private static native long nativeCreateBuilder(String sksl); + private static native long nativeCreateShader( + long shaderBuilder, long matrix, boolean isOpaque); + private static native void nativeUpdateUniforms( + long shaderBuilder, String uniformName, float[] uniforms); + private static native void nativeUpdateShader( + long shaderBuilder, String shaderName, long shader); } diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 48b474d6322e..f1866cdceae7 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -45,7 +45,6 @@ import android.text.FontConfig; import android.util.Base64; import android.util.LongSparseArray; import android.util.LruCache; -import android.util.Pair; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; @@ -197,7 +196,11 @@ public class Typeface { // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp /** @hide */ public static final int RESOLVE_BY_FONT_TABLE = -1; - private static final String DEFAULT_FAMILY = "sans-serif"; + /** + * The key of the default font family. + * @hide + */ + public static final String DEFAULT_FAMILY = "sans-serif"; // Style value for building typeface. private static final int STYLE_NORMAL = 0; @@ -1139,18 +1142,19 @@ public class Typeface { /** @hide */ @VisibleForTesting - public static void initSystemDefaultTypefaces(Map<String, Typeface> systemFontMap, - Map<String, FontFamily[]> fallbacks, - FontConfig.Alias[] aliases) { + public static void initSystemDefaultTypefaces(Map<String, FontFamily[]> fallbacks, + List<FontConfig.Alias> aliases, + Map<String, Typeface> outSystemFontMap) { for (Map.Entry<String, FontFamily[]> entry : fallbacks.entrySet()) { - systemFontMap.put(entry.getKey(), createFromFamilies(entry.getValue())); + outSystemFontMap.put(entry.getKey(), createFromFamilies(entry.getValue())); } - for (FontConfig.Alias alias : aliases) { - if (systemFontMap.containsKey(alias.getName())) { + for (int i = 0; i < aliases.size(); ++i) { + final FontConfig.Alias alias = aliases.get(i); + if (outSystemFontMap.containsKey(alias.getAliasName())) { continue; // If alias and named family are conflict, use named family. } - final Typeface base = systemFontMap.get(alias.getToName()); + final Typeface base = outSystemFontMap.get(alias.getReferName()); if (base == null) { // The missing target is a valid thing, some configuration don't have font files, // e.g. wear devices. Just skip this alias. @@ -1159,7 +1163,7 @@ public class Typeface { final int weight = alias.getWeight(); final Typeface newFace = weight == 400 ? base : new Typeface(nativeCreateWeightAlias(base.native_instance, weight)); - systemFontMap.put(alias.getName(), newFace); + outSystemFontMap.put(alias.getAliasName(), newFace); } } @@ -1339,11 +1343,11 @@ public class Typeface { /** @hide */ public static void loadPreinstalledSystemFontMap() { - final HashMap<String, Typeface> systemFontMap = new HashMap<>(); - Pair<FontConfig.Alias[], Map<String, FontFamily[]>> pair = - SystemFonts.initializePreinstalledFonts(); - initSystemDefaultTypefaces(systemFontMap, pair.second, pair.first); - setSystemFontMap(systemFontMap); + final FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig(); + final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig); + final Map<String, Typeface> typefaceMap = + SystemFonts.buildSystemTypefaces(fontConfig, fallback); + setSystemFontMap(typefaceMap); } static { diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java index f95da82ee07c..1ad6fbe264cb 100644 --- a/graphics/java/android/graphics/fonts/FontCustomizationParser.java +++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java @@ -16,18 +16,25 @@ package android.graphics.fonts; +import static android.text.FontConfig.Alias; +import static android.text.FontConfig.Family; + import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.FontListParser; -import android.text.FontConfig; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; -import java.util.HashSet; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * Parser for font customization @@ -39,8 +46,27 @@ public class FontCustomizationParser { * Represents a customization XML */ public static class Result { - ArrayList<FontConfig.Family> mAdditionalNamedFamilies = new ArrayList<>(); - ArrayList<FontConfig.Alias> mAdditionalAliases = new ArrayList<>(); + private final Map<String, Family> mAdditionalNamedFamilies; + private final List<Alias> mAdditionalAliases; + + public Result() { + mAdditionalNamedFamilies = Collections.emptyMap(); + mAdditionalAliases = Collections.emptyList(); + } + + public Result(Map<String, Family> additionalNamedFamilies, + List<Alias> additionalAliases) { + mAdditionalNamedFamilies = additionalNamedFamilies; + mAdditionalAliases = additionalAliases; + } + + public Map<String, Family> getAdditionalNamedFamilies() { + return mAdditionalNamedFamilies; + } + + public List<Alias> getAdditionalAliases() { + return mAdditionalAliases; + } } /** @@ -48,56 +74,67 @@ public class FontCustomizationParser { * * Caller must close the input stream */ - public static Result parse(@NonNull InputStream in, @NonNull String fontDir) - throws XmlPullParserException, IOException { + public static Result parse( + @NonNull InputStream in, + @NonNull String fontDir, + @Nullable Map<String, File> updatableFontMap + ) throws XmlPullParserException, IOException { XmlPullParser parser = Xml.newPullParser(); parser.setInput(in, null); parser.nextTag(); - return readFamilies(parser, fontDir); + return readFamilies(parser, fontDir, updatableFontMap); } - private static void validate(Result result) { - HashSet<String> familyNames = new HashSet<>(); - for (int i = 0; i < result.mAdditionalNamedFamilies.size(); ++i) { - final FontConfig.Family family = result.mAdditionalNamedFamilies.get(i); - final String name = family.getName(); + private static Map<String, Family> validateAndTransformToMap(List<Family> families) { + HashMap<String, Family> namedFamily = new HashMap<>(); + for (int i = 0; i < families.size(); ++i) { + final Family family = families.get(i); + final String name = family.getFallbackName(); if (name == null) { throw new IllegalArgumentException("new-named-family requires name attribute"); } - if (!familyNames.add(name)) { + if (namedFamily.put(name, family) != null) { throw new IllegalArgumentException( "new-named-family requires unique name attribute"); } } + return namedFamily; } - private static Result readFamilies(XmlPullParser parser, String fontDir) - throws XmlPullParserException, IOException { - Result out = new Result(); + private static Result readFamilies( + @NonNull XmlPullParser parser, + @NonNull String fontDir, + @Nullable Map<String, File> updatableFontMap + ) throws XmlPullParserException, IOException { + List<Family> families = new ArrayList<>(); + List<Alias> aliases = new ArrayList<>(); parser.require(XmlPullParser.START_TAG, null, "fonts-modification"); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) continue; String tag = parser.getName(); if (tag.equals("family")) { - readFamily(parser, fontDir, out); + readFamily(parser, fontDir, families, updatableFontMap); } else if (tag.equals("alias")) { - out.mAdditionalAliases.add(FontListParser.readAlias(parser)); + aliases.add(FontListParser.readAlias(parser)); } else { FontListParser.skip(parser); } } - validate(out); - return out; + return new Result(validateAndTransformToMap(families), aliases); } - private static void readFamily(XmlPullParser parser, String fontDir, Result out) + private static void readFamily( + @NonNull XmlPullParser parser, + @NonNull String fontDir, + @NonNull List<Family> out, + @Nullable Map<String, File> updatableFontMap) throws XmlPullParserException, IOException { final String customizationType = parser.getAttributeValue(null, "customizationType"); if (customizationType == null) { throw new IllegalArgumentException("customizationType must be specified"); } if (customizationType.equals("new-named-family")) { - out.mAdditionalNamedFamilies.add(FontListParser.readFamily(parser, fontDir, null)); + out.add(FontListParser.readFamily(parser, fontDir, updatableFontMap)); } else { throw new IllegalArgumentException("Unknown customizationType=" + customizationType); } diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java index 75ea12062929..2f0c26f06df7 100644 --- a/graphics/java/android/graphics/fonts/FontFamily.java +++ b/graphics/java/android/graphics/fonts/FontFamily.java @@ -18,6 +18,7 @@ package android.graphics.fonts; import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; import android.text.FontConfig; import com.android.internal.util.Preconditions; @@ -119,7 +120,7 @@ public final class FontFamily { nAddFont(builderPtr, mFonts.get(i).getNativePtr()); } final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback); - final FontFamily family = new FontFamily(mFonts, ptr); + final FontFamily family = new FontFamily(mFonts, langTags, variant, ptr); sFamilyRegistory.registerNativeAllocation(family, ptr); return family; } @@ -138,15 +139,36 @@ public final class FontFamily { } private final ArrayList<Font> mFonts; + private final String mLangTags; + private final int mVariant; private final long mNativePtr; // Use Builder instead. - private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) { + private FontFamily(@NonNull ArrayList<Font> fonts, String langTags, int variant, long ptr) { mFonts = fonts; + mLangTags = langTags; + mVariant = variant; mNativePtr = ptr; } /** + * Returns a BCP-47 compliant language tags associated with this font family. + * @hide + * @return a BCP-47 compliant language tag. + */ + public @Nullable String getLangTags() { + return mLangTags; + } + + /** + * @hide + * @return a family variant + */ + public int getVariant() { + return mVariant; + } + + /** * Returns a font * * @param index an index of the font diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index 16a53c25db08..54167b4b3042 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -23,11 +23,9 @@ import android.graphics.Typeface; import android.text.FontConfig; import android.util.ArrayMap; import android.util.Log; -import android.util.Pair; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ArrayUtils; import org.xmlpull.v1.XmlPullParserException; @@ -37,7 +35,6 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -52,6 +49,11 @@ public final class SystemFonts { private static final String TAG = "SystemFonts"; private static final String DEFAULT_FAMILY = "sans-serif"; + private static final String FONTS_XML = "/system/etc/fonts.xml"; + private static final String SYSTEM_FONT_DIR = "/system/fonts/"; + private static final String OEM_XML = "/product/etc/fonts_customization.xml"; + private static final String OEM_FONT_DIR = "/product/fonts/"; + private SystemFonts() {} // Do not instansiate. private static final Object LOCK = new Object(); @@ -88,12 +90,10 @@ public final class SystemFonts { } private static @NonNull Set<Font> collectAllFonts() { - final FontCustomizationParser.Result oemCustomization = - readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/"); - Map<String, FontFamily[]> map = new ArrayMap<>(); // TODO: use updated fonts - buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", null /* updatableFontDir */, - oemCustomization, map); + FontConfig fontConfig = getSystemPreinstalledFontConfig(); + Map<String, FontFamily[]> map = buildSystemFallback(fontConfig); + Set<Font> res = new HashSet<>(); for (FontFamily[] families : map.values()) { for (FontFamily family : families) { @@ -119,15 +119,16 @@ public final class SystemFonts { @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackMap, @NonNull Map<String, ByteBuffer> cache) { - final String languageTags = xmlFamily.getLanguages(); - final int variant = xmlFamily.getVariant(); + final String languageTags = xmlFamily.getLocaleList().toLanguageTags(); + final int variant = xmlFamily.getTextHeightVariant(); final ArrayList<FontConfig.Font> defaultFonts = new ArrayList<>(); - final ArrayMap<String, ArrayList<FontConfig.Font>> specificFallbackFonts = new ArrayMap<>(); + final ArrayMap<String, ArrayList<FontConfig.Font>> specificFallbackFonts = + new ArrayMap<>(); // Collect default fallback and specific fallback fonts. for (final FontConfig.Font font : xmlFamily.getFonts()) { - final String fallbackName = font.getFallbackFor(); + final String fallbackName = font.getFallback(); if (fallbackName == null) { defaultFonts.add(font); } else { @@ -141,19 +142,22 @@ public final class SystemFonts { } final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily( - xmlFamily.getName(), defaultFonts, languageTags, variant, cache); + xmlFamily.getFallbackName(), defaultFonts, languageTags, variant, cache); // Insert family into fallback map. for (int i = 0; i < fallbackMap.size(); i++) { - final ArrayList<FontConfig.Font> fallback = - specificFallbackFonts.get(fallbackMap.keyAt(i)); + String name = fallbackMap.keyAt(i); + final ArrayList<FontConfig.Font> fallback = specificFallbackFonts.get(name); if (fallback == null) { - if (defaultFamily != null) { + String familyName = xmlFamily.getFallbackName(); + if (defaultFamily != null + // do not add myself to the fallback chain. + && (familyName == null || !familyName.equals(name))) { fallbackMap.valueAt(i).add(defaultFamily); } } else { final FontFamily family = createFontFamily( - xmlFamily.getName(), fallback, languageTags, variant, cache); + xmlFamily.getFallbackName(), fallback, languageTags, variant, cache); if (family != null) { fallbackMap.valueAt(i).add(family); } else if (defaultFamily != null) { @@ -177,7 +181,7 @@ public final class SystemFonts { FontFamily.Builder b = null; for (int i = 0; i < fonts.size(); i++) { final FontConfig.Font fontConfig = fonts.get(i); - final String fullPath = fontConfig.getFontName(); + final String fullPath = fontConfig.getFilePath().getAbsolutePath(); ByteBuffer buffer = cache.get(fullPath); if (buffer == null) { if (cache.containsKey(fullPath)) { @@ -193,11 +197,10 @@ public final class SystemFonts { final Font font; try { font = new Font.Builder(buffer, new File(fullPath), languageTags) - .setWeight(fontConfig.getWeight()) - .setSlant(fontConfig.isItalic() ? FontStyle.FONT_SLANT_ITALIC - : FontStyle.FONT_SLANT_UPRIGHT) - .setTtcIndex(fontConfig.getTtcIndex()) - .setFontVariationSettings(fontConfig.getAxes()) + .setWeight(fontConfig.getStyle().getWeight()) + .setSlant(fontConfig.getStyle().getSlant()) + .setTtcIndex(fontConfig.getIndex()) + .setFontVariationSettings(fontConfig.getFontVariationSettings()) .build(); } catch (IOException e) { throw new RuntimeException(e); // Never reaches here @@ -215,10 +218,11 @@ public final class SystemFonts { private static void appendNamedFamily(@NonNull FontConfig.Family xmlFamily, @NonNull HashMap<String, ByteBuffer> bufferCache, @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap) { - final String familyName = xmlFamily.getName(); + final String familyName = xmlFamily.getFallbackName(); final FontFamily family = createFontFamily( - familyName, Arrays.asList(xmlFamily.getFonts()), - xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache); + familyName, xmlFamily.getFontList(), + xmlFamily.getLocaleList().toLanguageTags(), xmlFamily.getTextHeightVariant(), + bufferCache); if (family == null) { return; } @@ -228,116 +232,104 @@ public final class SystemFonts { } /** - * @see #buildSystemFallback(String, String, String, FontCustomizationParser.Result, Map) + * Get the updated FontConfig. + * + * @param updatableFontMap a font mapping of updated font files. * @hide */ - @VisibleForTesting - public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath, - @NonNull String fontDir, - @NonNull FontCustomizationParser.Result oemCustomization, - @NonNull Map<String, FontFamily[]> fallbackMap) { - return buildSystemFallback(xmlPath, fontDir, null /* updatableFontDir */, - oemCustomization, fallbackMap); + public static @NonNull FontConfig getSystemFontConfig( + @Nullable Map<String, File> updatableFontMap + ) { + return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, + updatableFontMap); } /** - * Build the system fallback from xml file. - * - * @param xmlPath A full path string to the fonts.xml file. - * @param fontDir A full path string to the system font directory. This must end with - * slash('/'). - * @param updatableFontDir A full path string to the updatable system font directory. This - * must end with slash('/'). - * @param fallbackMap An output system fallback map. Caller must pass empty map. - * @return a list of aliases + * Get the system preinstalled FontConfig. * @hide */ - @VisibleForTesting - public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath, - @NonNull String fontDir, - @Nullable String updatableFontDir, - @NonNull FontCustomizationParser.Result oemCustomization, - @NonNull Map<String, FontFamily[]> fallbackMap) { - try { - final FileInputStream fontsIn = new FileInputStream(xmlPath); - final FontConfig fontConfig = FontListParser.parse(fontsIn, fontDir, updatableFontDir); - - final HashMap<String, ByteBuffer> bufferCache = new HashMap<String, ByteBuffer>(); - final FontConfig.Family[] xmlFamilies = fontConfig.getFamilies(); - - final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>(); - // First traverse families which have a 'name' attribute to create fallback map. - for (final FontConfig.Family xmlFamily : xmlFamilies) { - final String familyName = xmlFamily.getName(); - if (familyName == null) { - continue; - } - appendNamedFamily(xmlFamily, bufferCache, fallbackListMap); - } + public static @NonNull FontConfig getSystemPreinstalledFontConfig() { + return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, null); + } - for (int i = 0; i < oemCustomization.mAdditionalNamedFamilies.size(); ++i) { - appendNamedFamily(oemCustomization.mAdditionalNamedFamilies.get(i), - bufferCache, fallbackListMap); - } + /* package */ static @NonNull FontConfig getSystemFontConfigInternal( + @NonNull String fontsXml, + @NonNull String systemFontDir, + @Nullable String oemXml, + @Nullable String productFontDir, + @Nullable Map<String, File> updatableFontMap + ) { + try { + return FontListParser.parse(fontsXml, systemFontDir, oemXml, productFontDir, + updatableFontMap); + } catch (IOException e) { + Log.e(TAG, "Failed to open/read system font configurations.", e); + return new FontConfig(Collections.emptyList(), Collections.emptyList()); + } catch (XmlPullParserException e) { + Log.e(TAG, "Failed to parse the system font configuration.", e); + return new FontConfig(Collections.emptyList(), Collections.emptyList()); + } + } - // Then, add fallback fonts to the each fallback map. - for (int i = 0; i < xmlFamilies.length; i++) { - final FontConfig.Family xmlFamily = xmlFamilies[i]; - // The first family (usually the sans-serif family) is always placed immediately - // after the primary family in the fallback. - if (i == 0 || xmlFamily.getName() == null) { - pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache); - } + /** + * Build the system fallback from FontConfig. + * @hide + */ + @VisibleForTesting + public static Map<String, FontFamily[]> buildSystemFallback(FontConfig fontConfig) { + final Map<String, FontFamily[]> fallbackMap = new HashMap<>(); + final HashMap<String, ByteBuffer> bufferCache = new HashMap<>(); + final List<FontConfig.Family> xmlFamilies = fontConfig.getFontFamilies(); + + final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>(); + // First traverse families which have a 'name' attribute to create fallback map. + for (final FontConfig.Family xmlFamily : xmlFamilies) { + final String familyName = xmlFamily.getFallbackName(); + if (familyName == null) { + continue; } + appendNamedFamily(xmlFamily, bufferCache, fallbackListMap); + } - // Build the font map and fallback map. - for (int i = 0; i < fallbackListMap.size(); i++) { - final String fallbackName = fallbackListMap.keyAt(i); - final List<FontFamily> familyList = fallbackListMap.valueAt(i); - final FontFamily[] families = familyList.toArray(new FontFamily[familyList.size()]); - - fallbackMap.put(fallbackName, families); + // Then, add fallback fonts to the each fallback map. + for (int i = 0; i < xmlFamilies.size(); i++) { + final FontConfig.Family xmlFamily = xmlFamilies.get(i); + // The first family (usually the sans-serif family) is always placed immediately + // after the primary family in the fallback. + if (i == 0 || xmlFamily.getFallbackName() == null) { + pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache); } - - final ArrayList<FontConfig.Alias> list = new ArrayList<>(); - list.addAll(Arrays.asList(fontConfig.getAliases())); - list.addAll(oemCustomization.mAdditionalAliases); - return list.toArray(new FontConfig.Alias[list.size()]); - } catch (IOException | XmlPullParserException e) { - Log.e(TAG, "Failed initialize system fallbacks.", e); - return ArrayUtils.emptyArray(FontConfig.Alias.class); } - } - private static FontCustomizationParser.Result readFontCustomization( - @NonNull String customizeXml, @NonNull String customFontsDir) { - try (FileInputStream f = new FileInputStream(customizeXml)) { - return FontCustomizationParser.parse(f, customFontsDir); - } catch (IOException e) { - return new FontCustomizationParser.Result(); - } catch (XmlPullParserException e) { - Log.e(TAG, "Failed to parse font customization XML", e); - return new FontCustomizationParser.Result(); + // Build the font map and fallback map. + for (int i = 0; i < fallbackListMap.size(); i++) { + final String fallbackName = fallbackListMap.keyAt(i); + final List<FontFamily> familyList = fallbackListMap.valueAt(i); + fallbackMap.put(fallbackName, familyList.toArray(new FontFamily[0])); } + + return fallbackMap; } - /** @hide */ - public static @NonNull Pair<FontConfig.Alias[], Map<String, FontFamily[]>> - initializePreinstalledFonts() { - return initializeSystemFonts(null); + /** + * Build the system Typeface mappings from FontConfig and FallbackMap. + * @hide + */ + @VisibleForTesting + public static Map<String, Typeface> buildSystemTypefaces( + FontConfig fontConfig, + Map<String, FontFamily[]> fallbackMap) { + final HashMap<String, Typeface> result = new HashMap<>(); + Typeface.initSystemDefaultTypefaces(fallbackMap, fontConfig.getAliases(), result); + return result; } - /** @hide */ - public static Pair<FontConfig.Alias[], Map<String, FontFamily[]>> - initializeSystemFonts(@Nullable String updatableFontDir) { - final FontCustomizationParser.Result oemCustomization = - readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/"); - Map<String, FontFamily[]> map = new ArrayMap<>(); - FontConfig.Alias[] aliases = buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", - updatableFontDir, oemCustomization, map); + /** + * @hide + */ + public void resetFallbackMapping(Map<String, FontFamily[]> fallbackMap) { synchronized (LOCK) { - sFamilyMap = map; + sFamilyMap = fallbackMap; } - return new Pair(aliases, map); } } diff --git a/keystore/java/android/security/AuthTokenUtils.java b/keystore/java/android/security/AuthTokenUtils.java new file mode 100644 index 000000000000..14d6626e5e97 --- /dev/null +++ b/keystore/java/android/security/AuthTokenUtils.java @@ -0,0 +1,75 @@ +/* + * 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.security; + +import android.annotation.NonNull; +import android.hardware.security.keymint.HardwareAuthToken; +import android.hardware.security.keymint.Timestamp; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * @hide This Utils class provides method(s) for AuthToken conversion. + */ +public class AuthTokenUtils { + + private AuthTokenUtils(){ + } + + /** + * Build a HardwareAuthToken from a byte array + * @param array byte array representing an auth token + * @return HardwareAuthToken representation of an auth token + */ + public static @NonNull HardwareAuthToken toHardwareAuthToken(@NonNull byte[] array) { + final HardwareAuthToken hardwareAuthToken = new HardwareAuthToken(); + + // First byte is version, which does not exist in HardwareAuthToken anymore + // Next 8 bytes is the challenge. + hardwareAuthToken.challenge = + ByteBuffer.wrap(array, 1, 8).order(ByteOrder.nativeOrder()).getLong(); + + // Next 8 bytes is the userId + hardwareAuthToken.userId = + ByteBuffer.wrap(array, 9, 8).order(ByteOrder.nativeOrder()).getLong(); + + // Next 8 bytes is the authenticatorId. + hardwareAuthToken.authenticatorId = + ByteBuffer.wrap(array, 17, 8).order(ByteOrder.nativeOrder()).getLong(); + + // while the other fields are in machine byte order, authenticatorType and timestamp + // are in network byte order. + // Next 4 bytes is the authenticatorType. + hardwareAuthToken.authenticatorType = + ByteBuffer.wrap(array, 25, 4).order(ByteOrder.BIG_ENDIAN).getInt(); + // Next 8 bytes is the timestamp. + final Timestamp timestamp = new Timestamp(); + timestamp.milliSeconds = + ByteBuffer.wrap(array, 29, 8).order(ByteOrder.BIG_ENDIAN).getLong(); + hardwareAuthToken.timestamp = timestamp; + + // Last 32 bytes is the mac, 37:69 + hardwareAuthToken.mac = new byte[32]; + System.arraycopy(array, 37 /* srcPos */, + hardwareAuthToken.mac, + 0 /* destPos */, + 32 /* length */); + + return hardwareAuthToken; + } +} diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java new file mode 100644 index 000000000000..1fde2b5412ed --- /dev/null +++ b/keystore/java/android/security/Authorization.java @@ -0,0 +1,78 @@ +/* + * 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.security; + +import android.annotation.NonNull; +import android.hardware.security.keymint.HardwareAuthToken; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.ServiceSpecificException; +import android.security.authorization.IKeystoreAuthorization; +import android.system.keystore2.ResponseCode; +import android.util.Log; + +/** + * @hide This is the client side for IKeystoreAuthorization AIDL. + * It shall only be used by biometric authentication providers and Gatekeeper. + */ +public class Authorization { + private static final String TAG = "KeystoreAuthorization"; + private static IKeystoreAuthorization sIKeystoreAuthorization; + + public static final int SYSTEM_ERROR = ResponseCode.SYSTEM_ERROR; + + public Authorization() { + sIKeystoreAuthorization = null; + } + + private static synchronized IKeystoreAuthorization getService() { + if (sIKeystoreAuthorization == null) { + sIKeystoreAuthorization = IKeystoreAuthorization.Stub.asInterface( + ServiceManager.checkService("android.security.authorization")); + } + return sIKeystoreAuthorization; + } + + /** + * Adds an auth token to keystore2. + * + * @param authToken created by Android authenticators. + * @return 0 if successful or {@code ResponseCode.SYSTEM_ERROR}. + */ + public int addAuthToken(@NonNull HardwareAuthToken authToken) { + if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0; + try { + getService().addAuthToken(authToken); + return 0; + } catch (RemoteException e) { + Log.w(TAG, "Can not connect to keystore", e); + return SYSTEM_ERROR; + } catch (ServiceSpecificException e) { + return e.errorCode; + } + } + + /** + * Add an auth token to Keystore 2.0 in the legacy serialized auth token format. + * @param authToken + * @return 0 if successful or a {@code ResponseCode}. + */ + public int addAuthToken(@NonNull byte[] authToken) { + return addAuthToken(AuthTokenUtils.toHardwareAuthToken(authToken)); + } + +} diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 2f444b34ce81..97819c56fd5a 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -17,10 +17,13 @@ package android.security; import static android.security.Credentials.ACTION_MANAGE_CREDENTIALS; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.TestApi; import android.annotation.WorkerThread; import android.app.Activity; import android.app.PendingIntent; @@ -41,6 +44,7 @@ import android.os.UserManager; import android.security.keystore.AndroidKeyStoreProvider; import android.security.keystore.KeyPermanentlyInvalidatedException; import android.security.keystore.KeyProperties; +import android.util.Log; import com.android.org.conscrypt.TrustedCertificateStore; @@ -105,6 +109,11 @@ import javax.security.auth.x500.X500Principal; public final class KeyChain { /** + * @hide + */ + public static final String LOG = "KeyChain"; + + /** * @hide Also used by KeyChainService implementation */ public static final String ACCOUNT_TYPE = "com.android.keychain"; @@ -579,6 +588,55 @@ public final class KeyChain { activity.startActivity(intent); } + /** + * Set a credential management app. The credential management app has the ability to manage + * the user's KeyChain credentials on unmanaged devices. + * + * <p>There can only be one credential management on the device. If another app requests to + * become the credential management app, then the existing credential management app will + * no longer be able to manage credentials. + * + * @param packageName The package name of the credential management app + * @param authenticationPolicy The authentication policy of the credential management app. This + * policy determines which alias for a private key and certificate + * pair should be used for authentication. + * @return {@code true} if the credential management app was successfully added. + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP) + public static boolean setCredentialManagementApp(@NonNull Context context, + @NonNull String packageName, @NonNull AppUriAuthenticationPolicy authenticationPolicy) { + try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) { + keyChainConnection.getService() + .setCredentialManagementApp(packageName, authenticationPolicy); + return true; + } catch (RemoteException | InterruptedException e) { + Log.w(LOG, "Set credential management app failed", e); + Thread.currentThread().interrupt(); + return false; + } + } + + /** + * Remove the user's KeyChain credentials on unmanaged devices. + * + * @return {@code true} if the credential management app was successfully removed. + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP) + public static boolean removeCredentialManagementApp(@NonNull Context context) { + try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) { + keyChainConnection.getService().removeCredentialManagementApp(); + return true; + } catch (RemoteException | InterruptedException e) { + Log.w(LOG, "Remove credential management app failed", e); + Thread.currentThread().interrupt(); + return false; + } + } + private static class AliasResponse extends IKeyChainAliasCallback.Stub { private final KeyChainAliasCallback keyChainAliasResponse; private AliasResponse(KeyChainAliasCallback keyChainAliasResponse) { diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index c70c986fcd6b..4a67135227dd 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -996,6 +996,7 @@ public class KeyStore { */ public int addAuthToken(byte[] authToken) { try { + new Authorization().addAuthToken(authToken); return mBinder.addAuthToken(authToken); } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationChunkedStreamer.java index 6c733ba712d5..33e8dede9f5c 100644 --- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationChunkedStreamer.java +++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationChunkedStreamer.java @@ -139,7 +139,9 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS int inputConsumed = ArrayUtils.copy(input, inputOffset, mChunk, mChunkLength, inputLength); inputLength -= inputConsumed; - inputOffset += inputOffset; + inputOffset += inputConsumed; + mChunkLength += inputConsumed; + if (mChunkLength < mChunkSizeMax) return output; byte[] o = mKeyStoreStream.update(mChunk); if (o != null) { output = ArrayUtils.concat(output, o); diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json index 0290d9f4b316..2cfb13e7dea6 100644 --- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json +++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json @@ -1,6 +1,12 @@ { "version": "1.0.0", "messages": { + "-2076257741": { + "message": "Transition requested: %s %s", + "level": "VERBOSE", + "group": "WM_SHELL_TRANSITIONS", + "at": "com\/android\/wm\/shell\/transition\/Transitions.java" + }, "-1683614271": { "message": "Existing task: id=%d component=%s", "level": "VERBOSE", @@ -133,12 +139,6 @@ "group": "WM_SHELL_TASK_ORG", "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" }, - "846958769": { - "message": "Transition requested: type=%d %s", - "level": "VERBOSE", - "group": "WM_SHELL_TRANSITIONS", - "at": "com\/android\/wm\/shell\/transition\/Transitions.java" - }, "900599280": { "message": "Can't pair unresizeable tasks task1.isResizeable=%b task1.isResizeable=%b", "level": "ERROR", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java index 0146b728bcad..7aedc1b50db0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java @@ -71,6 +71,7 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { + if (Transitions.ENABLE_SHELL_TRANSITIONS) return; final SurfaceControl leash = mLeashByTaskId.get(taskInfo.taskId); final Point positionInParent = taskInfo.positionInParent; mSyncQueue.runInSync(t -> t.setPosition(leash, positionInParent.x, positionInParent.y)); 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 faa4a0ed2294..c9b38d00c0ae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -262,12 +262,6 @@ public class ShellTaskOrganizer extends TaskOrganizer { synchronized (mLock) { ProtoLog.v(WM_SHELL_TASK_ORG, "Task info changed taskId=%d", taskInfo.taskId); final TaskAppearedInfo data = mTasks.get(taskInfo.taskId); - if (data == null) { - // TODO(b/171749427): It means onTaskInfoChanged send before onTaskAppeared or - // after onTaskVanished, it should be fixed in controller side. - return; - } - final TaskListener oldListener = getTaskListener(data.getTaskInfo()); final TaskListener newListener = getTaskListener(taskInfo); mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, data.getLeash())); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java index 8d0e9655f28d..dd4313957f20 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java @@ -252,7 +252,9 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true); // TODO: Synchronize show with the resize onLocationChanged(); - setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); + if (taskInfo.taskDescription != null) { + setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); + } if (mListener != null) { mListenerExecutor.execute(() -> { @@ -279,8 +281,9 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { - mTaskInfo.taskDescription = taskInfo.taskDescription; - setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); + if (taskInfo.taskDescription != null) { + setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); + } } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java index 94b2cc0455bd..d066cf92650c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java @@ -36,6 +36,7 @@ import android.os.IBinder; import android.view.SurfaceControl; import android.view.WindowManager; import android.window.TransitionInfo; +import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; import com.android.wm.shell.common.TransactionPool; @@ -76,9 +77,11 @@ public class SplitScreenTransitions implements Transitions.TransitionHandler { } @Override - public WindowContainerTransaction handleRequest(@WindowManager.TransitionType int type, - @NonNull IBinder transition, @Nullable ActivityManager.RunningTaskInfo triggerTask) { + public WindowContainerTransaction handleRequest(@NonNull IBinder transition, + @Nullable TransitionRequestInfo request) { WindowContainerTransaction out = null; + final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask(); + final @WindowManager.TransitionType int type = request.getType(); if (mSplitScreen.isDividerVisible()) { // try to handle everything while in split-screen out = new WindowContainerTransaction(); 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 821a00703adf..e95864873c0c 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 @@ -49,9 +49,9 @@ public interface OneHanded { void stopOneHanded(); /** - * Exits one handed mode with {@link OneHandedEvents}. + * Exits one handed mode with {@link OneHandedUiEventLogger}. */ - void stopOneHanded(int event); + void stopOneHanded(int uiEvent); /** * Set navigation 3 button mode enabled or disabled by users. 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 48d6a7b40ee1..eaa704f22410 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 @@ -37,6 +37,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.android.internal.logging.UiEventLogger; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; @@ -47,7 +48,6 @@ import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback; import java.io.PrintWriter; -import java.util.concurrent.Executor; /** * Manages and manipulates the one handed states, transitions, and gesture for phones. @@ -73,6 +73,8 @@ public class OneHandedController { private final OneHandedTimeoutHandler mTimeoutHandler; private final OneHandedTouchHandler mTouchHandler; private final OneHandedTutorialHandler mTutorialHandler; + private final OneHandedUiEventLogger mOneHandedUiEventLogger; + private final TaskStackListenerImpl mTaskStackListener; private final IOverlayManager mOverlayManager; private final ShellExecutor mMainExecutor; private final Handler mMainHandler; @@ -117,15 +119,28 @@ public class OneHandedController { } }; + private final TaskStackListenerCallback mTaskStackListenerCallback = + new TaskStackListenerCallback() { + @Override + public void onTaskCreated(int taskId, ComponentName componentName) { + stopOneHanded(OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT); + } + + @Override + public void onTaskMovedToFront(int taskId) { + stopOneHanded(OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT); + } + }; + + /** * Creates {@link OneHanded}, returns {@code null} if the feature is not supported. */ @Nullable public static OneHanded create( Context context, DisplayController displayController, - TaskStackListenerImpl taskStackListener, - ShellExecutor mainExecutor, - Handler mainHandler) { + TaskStackListenerImpl taskStackListener, UiEventLogger uiEventLogger, + ShellExecutor mainExecutor, Handler mainHandler) { if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) { Slog.w(TAG, "Device doesn't support OneHanded feature"); return null; @@ -145,12 +160,13 @@ public class OneHandedController { OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer( context, displayController, animationController, tutorialHandler, oneHandedBackgroundPanelOrganizer, mainExecutor); + OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger); IOverlayManager overlayManager = IOverlayManager.Stub.asInterface( ServiceManager.getService(Context.OVERLAY_SERVICE)); return new OneHandedController(context, displayController, oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler, - gestureHandler, timeoutHandler, overlayManager, taskStackListener, mainExecutor, - mainHandler).mImpl; + gestureHandler, timeoutHandler, oneHandedUiEventsLogger, overlayManager, + taskStackListener, mainExecutor, mainHandler).mImpl; } @VisibleForTesting @@ -162,6 +178,7 @@ public class OneHandedController { OneHandedTutorialHandler tutorialHandler, OneHandedGestureHandler gestureHandler, OneHandedTimeoutHandler timeoutHandler, + OneHandedUiEventLogger uiEventsLogger, IOverlayManager overlayManager, TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor, @@ -176,6 +193,8 @@ public class OneHandedController { mOverlayManager = overlayManager; mMainExecutor = mainExecutor; mMainHandler = mainHandler; + mOneHandedUiEventLogger = uiEventsLogger; + mTaskStackListener = taskStackListener; final float offsetPercentageConfig = context.getResources().getFraction( R.fraction.config_one_handed_offset, 1, 1); @@ -203,19 +222,6 @@ public class OneHandedController { setupGesturalOverlay(); updateSettings(); - taskStackListener.addListener( - new TaskStackListenerCallback() { - @Override - public void onTaskCreated(int taskId, ComponentName componentName) { - stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT); - } - - @Override - public void onTaskMovedToFront(int taskId) { - stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT); - } - }); - mAccessibilityManager = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); mAccessibilityManager.addAccessibilityStateChangeListener( @@ -234,6 +240,11 @@ public class OneHandedController { * Set one handed enabled or disabled by when user update settings */ void setTaskChangeToExit(boolean enabled) { + if (enabled) { + mTaskStackListener.addListener(mTaskStackListenerCallback); + } else { + mTaskStackListener.removeListener(mTaskStackListenerCallback); + } mTaskChangeToExit = enabled; } @@ -252,7 +263,8 @@ public class OneHandedController { mDisplayAreaOrganizer.scheduleOffset(0, yOffSet); mTimeoutHandler.resetTimer(); - OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN); + mOneHandedUiEventLogger.writeEvent( + OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN); } } @@ -264,17 +276,12 @@ public class OneHandedController { } } - private void stopOneHanded(int event) { - if (!mTaskChangeToExit && event == OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT) { - //Task change exit not enable, do nothing and return here. - return; - } - + private void stopOneHanded(int uiEvent) { if (mDisplayAreaOrganizer.isInOneHanded()) { - OneHandedEvents.writeEvent(event); + mDisplayAreaOrganizer.scheduleOffset(0, 0); + mTimeoutHandler.removeTimer(); + mOneHandedUiEventLogger.writeEvent(uiEvent); } - - stopOneHanded(); } private void setThreeButtonModeEnabled(boolean enabled) { @@ -292,11 +299,14 @@ public class OneHandedController { private void setupCallback() { mTouchHandler.registerTouchEventListener(() -> - stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_OVERSPACE_OUT)); + stopOneHanded(OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_OVERSPACE_OUT)); mDisplayAreaOrganizer.registerTransitionCallback(mTouchHandler); mDisplayAreaOrganizer.registerTransitionCallback(mGestureHandler); mDisplayAreaOrganizer.registerTransitionCallback(mTutorialHandler); mDisplayAreaOrganizer.registerTransitionCallback(mBackgroundPanelOrganizer); + if (mTaskChangeToExit) { + mTaskStackListener.addListener(mTaskStackListenerCallback); + } } private void setupSettingObservers() { @@ -334,9 +344,9 @@ public class OneHandedController { private void onEnabledSettingChanged() { final boolean enabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( mContext.getContentResolver()); - OneHandedEvents.writeEvent(enabled - ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON - : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF); + mOneHandedUiEventLogger.writeEvent(enabled + ? OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON + : OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF); setOneHandedEnabled(enabled); @@ -349,25 +359,25 @@ public class OneHandedController { private void onTimeoutSettingChanged() { final int newTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout( mContext.getContentResolver()); - int metricsId = OneHandedEvents.OneHandedSettingsTogglesEvent.INVALID.getId(); + int metricsId = OneHandedUiEventLogger.OneHandedSettingsTogglesEvent.INVALID.getId(); switch (newTimeout) { case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER: - metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_NEVER; + metricsId = OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_NEVER; break; case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS: - metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_4; + metricsId = OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_4; break; case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS: - metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_8; + metricsId = OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_8; break; case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS: - metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12; + metricsId = OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12; break; default: // do nothing break; } - OneHandedEvents.writeEvent(metricsId); + mOneHandedUiEventLogger.writeEvent(metricsId); if (mTimeoutHandler != null) { mTimeoutHandler.setTimeout(newTimeout); @@ -377,9 +387,9 @@ public class OneHandedController { private void onTaskChangeExitSettingChanged() { final boolean enabled = OneHandedSettingsUtil.getSettingsTapsAppToExit( mContext.getContentResolver()); - OneHandedEvents.writeEvent(enabled - ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON - : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF); + mOneHandedUiEventLogger.writeEvent(enabled + ? OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON + : OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF); setTaskChangeToExit(enabled); } @@ -397,9 +407,8 @@ public class OneHandedController { } private void setupTimeoutListener() { - mTimeoutHandler.registerTimeoutListener(timeoutTime -> { - stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_TIMEOUT_OUT); - }); + mTimeoutHandler.registerTimeoutListener(timeoutTime -> stopOneHanded( + OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_TIMEOUT_OUT)); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEvents.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedUiEventLogger.java index 79ddd2b11e72..38ffb07aa5a4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEvents.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedUiEventLogger.java @@ -19,17 +19,13 @@ package com.android.wm.shell.onehanded; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; -import com.android.internal.logging.UiEventLoggerImpl; /** - * Interesting events related to the One-Handed. + * Helper class that ends OneHanded mode log to UiEvent, see also go/uievent */ -public class OneHandedEvents { - private static final String TAG = "OneHandedEvents"; - - public static Callback sCallback; - @VisibleForTesting - static UiEventLogger sUiEventLogger = new UiEventLoggerImpl(); +public class OneHandedUiEventLogger { + private static final String TAG = "OneHandedUiEventLogger"; + private final UiEventLogger mUiEventLogger; /** * One-Handed event types @@ -76,6 +72,10 @@ public class OneHandedEvents { "one_handed_settings_timeout_seconds_12" }; + public OneHandedUiEventLogger(UiEventLogger uiEventLogger) { + mUiEventLogger = uiEventLogger; + } + /** * Events definition that related to One-Handed gestures. */ @@ -112,6 +112,7 @@ public class OneHandedEvents { mId = id; } + @Override public int getId() { return mId; } @@ -159,6 +160,7 @@ public class OneHandedEvents { mId = id; } + @Override public int getId() { return mId; } @@ -169,12 +171,8 @@ public class OneHandedEvents { * Logs an event to the system log, to sCallback if present, and to the logEvent destinations. * @param tag One of the EVENT_* codes above. */ - public static void writeEvent(int tag) { - final long time = System.currentTimeMillis(); + public void writeEvent(int tag) { logEvent(tag); - if (sCallback != null) { - sCallback.writeEvent(time, tag); - } } /** @@ -183,94 +181,75 @@ public class OneHandedEvents { * @return String a readable description of the event. Begins "writeEvent <tag_description>" * if the tag is valid. */ - public static String logEvent(int event) { - if (event >= EVENT_TAGS.length) { - return ""; - } - final StringBuilder sb = new StringBuilder("writeEvent ").append(EVENT_TAGS[event]); + private void logEvent(int event) { switch (event) { - // Triggers case EVENT_ONE_HANDED_TRIGGER_GESTURE_IN: - sUiEventLogger.log(OneHandedTriggerEvent.ONE_HANDED_TRIGGER_GESTURE_IN); + mUiEventLogger.log(OneHandedTriggerEvent.ONE_HANDED_TRIGGER_GESTURE_IN); break; case EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT: - sUiEventLogger.log(OneHandedTriggerEvent.ONE_HANDED_TRIGGER_GESTURE_OUT); + mUiEventLogger.log(OneHandedTriggerEvent.ONE_HANDED_TRIGGER_GESTURE_OUT); break; case EVENT_ONE_HANDED_TRIGGER_OVERSPACE_OUT: - sUiEventLogger.log(OneHandedTriggerEvent.ONE_HANDED_TRIGGER_OVERSPACE_OUT); + mUiEventLogger.log(OneHandedTriggerEvent.ONE_HANDED_TRIGGER_OVERSPACE_OUT); break; case EVENT_ONE_HANDED_TRIGGER_POP_IME_OUT: - sUiEventLogger.log(OneHandedTriggerEvent.ONE_HANDED_TRIGGER_POP_IME_OUT); + mUiEventLogger.log(OneHandedTriggerEvent.ONE_HANDED_TRIGGER_POP_IME_OUT); break; case EVENT_ONE_HANDED_TRIGGER_ROTATION_OUT: - sUiEventLogger.log(OneHandedTriggerEvent.ONE_HANDED_TRIGGER_ROTATION_OUT); + mUiEventLogger.log(OneHandedTriggerEvent.ONE_HANDED_TRIGGER_ROTATION_OUT); break; case EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT: - sUiEventLogger.log(OneHandedTriggerEvent.ONE_HANDED_TRIGGER_APP_TAPS_OUT); + mUiEventLogger.log(OneHandedTriggerEvent.ONE_HANDED_TRIGGER_APP_TAPS_OUT); break; case EVENT_ONE_HANDED_TRIGGER_TIMEOUT_OUT: - sUiEventLogger.log(OneHandedTriggerEvent.ONE_HANDED_TRIGGER_TIMEOUT_OUT); + mUiEventLogger.log(OneHandedTriggerEvent.ONE_HANDED_TRIGGER_TIMEOUT_OUT); break; case EVENT_ONE_HANDED_TRIGGER_SCREEN_OFF_OUT: - sUiEventLogger.log(OneHandedTriggerEvent.ONE_HANDED_TRIGGER_SCREEN_OFF_OUT); + mUiEventLogger.log(OneHandedTriggerEvent.ONE_HANDED_TRIGGER_SCREEN_OFF_OUT); break; - // Settings case EVENT_ONE_HANDED_SETTINGS_ENABLED_ON: - sUiEventLogger.log(OneHandedSettingsTogglesEvent + mUiEventLogger.log(OneHandedSettingsTogglesEvent .ONE_HANDED_SETTINGS_TOGGLES_ENABLED_ON); break; case EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF: - sUiEventLogger.log(OneHandedSettingsTogglesEvent + mUiEventLogger.log(OneHandedSettingsTogglesEvent .ONE_HANDED_SETTINGS_TOGGLES_ENABLED_OFF); break; case EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON: - sUiEventLogger.log(OneHandedSettingsTogglesEvent + mUiEventLogger.log(OneHandedSettingsTogglesEvent .ONE_HANDED_SETTINGS_TOGGLES_APP_TAPS_EXIT_ON); break; case EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF: - sUiEventLogger.log(OneHandedSettingsTogglesEvent + mUiEventLogger.log(OneHandedSettingsTogglesEvent .ONE_HANDED_SETTINGS_TOGGLES_APP_TAPS_EXIT_OFF); break; case EVENT_ONE_HANDED_SETTINGS_TIMEOUT_EXIT_ON: - sUiEventLogger.log(OneHandedSettingsTogglesEvent + mUiEventLogger.log(OneHandedSettingsTogglesEvent .ONE_HANDED_SETTINGS_TOGGLES_TIMEOUT_EXIT_ON); break; case EVENT_ONE_HANDED_SETTINGS_TIMEOUT_EXIT_OFF: - sUiEventLogger.log(OneHandedSettingsTogglesEvent + mUiEventLogger.log(OneHandedSettingsTogglesEvent .ONE_HANDED_SETTINGS_TOGGLES_TIMEOUT_EXIT_OFF); break; case EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_NEVER: - sUiEventLogger.log(OneHandedSettingsTogglesEvent + mUiEventLogger.log(OneHandedSettingsTogglesEvent .ONE_HANDED_SETTINGS_TOGGLES_TIMEOUT_SECONDS_NEVER); break; case EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_4: - sUiEventLogger.log(OneHandedSettingsTogglesEvent + mUiEventLogger.log(OneHandedSettingsTogglesEvent .ONE_HANDED_SETTINGS_TOGGLES_TIMEOUT_SECONDS_4); break; case EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_8: - sUiEventLogger.log(OneHandedSettingsTogglesEvent + mUiEventLogger.log(OneHandedSettingsTogglesEvent .ONE_HANDED_SETTINGS_TOGGLES_TIMEOUT_SECONDS_8); break; case EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12: - sUiEventLogger.log(OneHandedSettingsTogglesEvent + mUiEventLogger.log(OneHandedSettingsTogglesEvent .ONE_HANDED_SETTINGS_TOGGLES_TIMEOUT_SECONDS_12); break; default: // Do nothing break; } - return sb.toString(); - } - - /** - * An interface for logging an event to the system log, if Callback present. - */ - public interface Callback { - /** - * - * @param time System current time. - * @param tag Event tag. - */ - void writeEvent(long time, int tag); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java index de3bb2950c0a..f8b4dd9bc621 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java @@ -41,12 +41,12 @@ public class PipUiEventLogger { } public void setTaskInfo(TaskInfo taskInfo) { - if (taskInfo == null) { - mPackageName = null; - mPackageUid = INVALID_PACKAGE_UID; - } else { + if (taskInfo != null && taskInfo.topActivity != null) { mPackageName = taskInfo.topActivity.getPackageName(); mPackageUid = getUid(mPackageName, taskInfo.userId); + } else { + mPackageName = null; + mPackageUid = INVALID_PACKAGE_UID; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 4cd2c504c83e..f1e06f7f5724 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -26,11 +26,11 @@ import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityManager; import android.os.IBinder; import android.util.ArrayMap; import android.view.SurfaceControl; import android.window.TransitionInfo; +import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; import com.android.wm.shell.common.ShellExecutor; @@ -99,8 +99,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { @Nullable @Override - public WindowContainerTransaction handleRequest(int type, @NonNull IBinder transition, - @Nullable ActivityManager.RunningTaskInfo triggerTask) { + public WindowContainerTransaction handleRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request) { return null; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java new file mode 100644 index 000000000000..cf141c6a4826 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.transition; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.ArrayMap; +import android.util.Log; +import android.util.Pair; +import android.view.IRemoteAnimationFinishedCallback; +import android.view.SurfaceControl; +import android.window.IRemoteTransition; +import android.window.TransitionFilter; +import android.window.TransitionInfo; +import android.window.TransitionRequestInfo; +import android.window.WindowContainerTransaction; + +import com.android.wm.shell.common.ShellExecutor; + +import java.util.ArrayList; + +/** + * Handler that deals with RemoteTransitions. It will only request to handle a transition + * if the request includes a specific remote. + */ +public class RemoteTransitionHandler implements Transitions.TransitionHandler { + private final ShellExecutor mMainExecutor; + + /** Includes remotes explicitly requested by, eg, ActivityOptions */ + private final ArrayMap<IBinder, IRemoteTransition> mPendingRemotes = new ArrayMap<>(); + + /** Ordered by specificity. Last filters will be checked first */ + private final ArrayList<Pair<TransitionFilter, IRemoteTransition>> mFilters = + new ArrayList<>(); + + RemoteTransitionHandler(@NonNull ShellExecutor mainExecutor) { + mMainExecutor = mainExecutor; + } + + void addFiltered(TransitionFilter filter, IRemoteTransition remote) { + mFilters.add(new Pair<TransitionFilter, IRemoteTransition>(filter, remote)); + } + + void removeFiltered(IRemoteTransition remote) { + for (int i = mFilters.size() - 1; i >= 0; --i) { + if (mFilters.get(i).second == remote) { + mFilters.remove(i); + } + } + } + + @Override + public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction t, @NonNull Runnable finishCallback) { + IRemoteTransition pendingRemote = mPendingRemotes.remove(transition); + if (pendingRemote == null) { + // If no explicit remote, search filters until one matches + for (int i = mFilters.size() - 1; i >= 0; --i) { + if (mFilters.get(i).first.matches(info)) { + pendingRemote = mFilters.get(i).second; + break; + } + } + } + + if (pendingRemote == null) return false; + + final IRemoteTransition remote = pendingRemote; + final IBinder.DeathRecipient remoteDied = () -> { + Log.e(Transitions.TAG, "Remote transition died, finishing"); + mMainExecutor.execute(finishCallback); + }; + IRemoteAnimationFinishedCallback cb = new IRemoteAnimationFinishedCallback.Stub() { + @Override + public void onAnimationFinished() throws RemoteException { + if (remote.asBinder() != null) { + remote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */); + } + mMainExecutor.execute(finishCallback); + } + }; + try { + if (remote.asBinder() != null) { + remote.asBinder().linkToDeath(remoteDied, 0 /* flags */); + } + remote.startAnimation(info, t, cb); + } catch (RemoteException e) { + Log.e(Transitions.TAG, "Error running remote transition.", e); + mMainExecutor.execute(finishCallback); + } + return true; + } + + @Override + @Nullable + public WindowContainerTransaction handleRequest(@NonNull IBinder transition, + @Nullable TransitionRequestInfo request) { + IRemoteTransition remote = request.getRemoteTransition(); + if (remote == null) return null; + mPendingRemotes.put(transition, remote); + return new WindowContainerTransaction(); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 3b2ac70007e4..c085168a3317 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -25,15 +25,18 @@ import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPI import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityManager; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemProperties; import android.util.ArrayMap; +import android.util.Log; import android.view.SurfaceControl; import android.view.WindowManager; +import android.window.IRemoteTransition; import android.window.ITransitionPlayer; +import android.window.TransitionFilter; import android.window.TransitionInfo; +import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; import android.window.WindowOrganizer; @@ -44,6 +47,7 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.util.ArrayList; @@ -51,7 +55,7 @@ import java.util.Arrays; /** Plays transition animations */ public class Transitions { - private static final String TAG = "ShellTransitions"; + static final String TAG = "ShellTransitions"; /** Set to {@code true} to enable shell transitions. */ public static final boolean ENABLE_SHELL_TRANSITIONS = @@ -61,6 +65,7 @@ public class Transitions { private final ShellExecutor mMainExecutor; private final ShellExecutor mAnimExecutor; private final TransitionPlayerImpl mPlayerImpl; + private final RemoteTransitionHandler mRemoteTransitionHandler; /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */ private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>(); @@ -80,10 +85,28 @@ public class Transitions { mPlayerImpl = new TransitionPlayerImpl(); // The very last handler (0 in the list) should be the default one. mHandlers.add(new DefaultTransitionHandler(pool, mainExecutor, animExecutor)); + // Next lowest priority is remote transitions. + mRemoteTransitionHandler = new RemoteTransitionHandler(mainExecutor); + mHandlers.add(mRemoteTransitionHandler); + } + + private Transitions() { + mOrganizer = null; + mMainExecutor = null; + mAnimExecutor = null; + mPlayerImpl = null; + mRemoteTransitionHandler = null; + } + + /** Create an empty/non-registering transitions object for system-ui tests. */ + @VisibleForTesting + public static Transitions createEmptyForTesting() { + return new Transitions(); } /** Register this transition handler with Core */ public void register(ShellTaskOrganizer taskOrganizer) { + if (mPlayerImpl == null) return; taskOrganizer.registerTransitionPlayer(mPlayerImpl); } @@ -109,6 +132,19 @@ public class Transitions { mHandlers.set(0, handler); } + /** Register a remote transition to be used when `filter` matches an incoming transition */ + @ExternalThread + public void registerRemote(@NonNull TransitionFilter filter, + @NonNull IRemoteTransition remoteTransition) { + mMainExecutor.execute(() -> mRemoteTransitionHandler.addFiltered(filter, remoteTransition)); + } + + /** Unregisters a remote transition and all associated filters */ + @ExternalThread + public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) { + mMainExecutor.execute(() -> mRemoteTransitionHandler.removeFiltered(remoteTransition)); + } + /** @return true if the transition was triggered by opening something vs closing something */ public static boolean isOpeningType(@WindowManager.TransitionType int type) { return type == TRANSIT_OPEN @@ -126,6 +162,8 @@ public class Transitions { if (info.getRootLeash().isValid()) { t.show(info.getRootLeash()); } + // Put animating stuff above this line and put static stuff below it. + int zSplitLine = info.getChanges().size(); // changes should be ordered top-to-bottom in z for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); @@ -152,7 +190,7 @@ public class Transitions { t.setMatrix(leash, 1, 0, 0, 1); if (isOpening) { // put on top with 0 alpha - t.setLayer(leash, info.getChanges().size() - i); + t.setLayer(leash, zSplitLine + info.getChanges().size() - i); if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { // This received a transferred starting window, so make it immediately // visible. @@ -162,19 +200,19 @@ public class Transitions { } } else { // put on bottom and leave it visible - t.setLayer(leash, -i); + t.setLayer(leash, zSplitLine - i); t.setAlpha(leash, 1.f); } } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) { if (isOpening) { // put on bottom and leave visible - t.setLayer(leash, -i); + t.setLayer(leash, zSplitLine - i); } else { // put on top - t.setLayer(leash, info.getChanges().size() - i); + t.setLayer(leash, zSplitLine + info.getChanges().size() - i); } } else { // CHANGE - t.setLayer(leash, info.getChanges().size() - i); + t.setLayer(leash, zSplitLine + info.getChanges().size() - i); } } } @@ -219,7 +257,8 @@ public class Transitions { private void onFinish(IBinder transition) { if (!mActiveTransitions.containsKey(transition)) { - throw new IllegalStateException("Trying to finish an already-finished transition."); + Log.e(TAG, "Trying to finish a non-running transition. Maybe remote crashed?"); + return; } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition animations finished, notifying core %s", transition); @@ -227,24 +266,24 @@ public class Transitions { mOrganizer.finishTransition(transition, null, null); } - void requestStartTransition(int type, @NonNull IBinder transitionToken, - @Nullable ActivityManager.RunningTaskInfo triggerTask) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: type=%d %s", - type, transitionToken); - + void requestStartTransition(@NonNull IBinder transitionToken, + @Nullable TransitionRequestInfo request) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: %s %s", + transitionToken, request); if (mActiveTransitions.containsKey(transitionToken)) { throw new RuntimeException("Transition already started " + transitionToken); } final ActiveTransition active = new ActiveTransition(); WindowContainerTransaction wct = null; for (int i = mHandlers.size() - 1; i >= 0; --i) { - wct = mHandlers.get(i).handleRequest(type, transitionToken, triggerTask); + wct = mHandlers.get(i).handleRequest(transitionToken, request); if (wct != null) { active.mFirstHandler = mHandlers.get(i); break; } } - IBinder transition = mOrganizer.startTransition(type, transitionToken, wct); + IBinder transition = mOrganizer.startTransition( + request.getType(), transitionToken, wct); mActiveTransitions.put(transition, active); } @@ -267,6 +306,7 @@ public class Transitions { * for a particular transition. Otherwise, it is only called if no other handler before * it handled the transition. * + * @param finishCallback Call this when finished. This MUST be called on main thread. * @return true if transition was handled, false if not (falls-back to default). */ boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @@ -274,15 +314,15 @@ public class Transitions { /** * Potentially handles a startTransition request. - * @param type The transition type - * @param triggerTask The task which triggered this transition request. - * @return WCT to apply with transition-start or null if this handler isn't handling - * the request. + * + * @param transition The transition whose start is being requested. + * @param request Information about what is requested. + * @return WCT to apply with transition-start or null. If a WCT is returned here, this + * handler will be the first in line to animate. */ @Nullable - WindowContainerTransaction handleRequest(@WindowManager.TransitionType int type, - @NonNull IBinder transition, - @Nullable ActivityManager.RunningTaskInfo triggerTask); + WindowContainerTransaction handleRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request); } @BinderThread @@ -296,11 +336,9 @@ public class Transitions { } @Override - public void requestStartTransition(int i, IBinder iBinder, - ActivityManager.RunningTaskInfo runningTaskInfo) throws RemoteException { - mMainExecutor.execute(() -> { - Transitions.this.requestStartTransition(i, iBinder, runningTaskInfo); - }); + public void requestStartTransition(IBinder iBinder, + TransitionRequestInfo request) throws RemoteException { + mMainExecutor.execute(() -> Transitions.this.requestStartTransition(iBinder, request)); } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt index 85bf4a1f8c25..05e453998078 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt @@ -155,7 +155,9 @@ class EnterLegacySplitScreenTest( showsAppWindow(splitScreenApp.defaultWindowName) .and().showsAppWindow(secondaryApp.defaultWindowName) } - visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(LAUNCHER_PACKAGE_NAME)) + visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + secondaryApp.defaultWindowName)) } } } @@ -187,7 +189,8 @@ class EnterLegacySplitScreenTest( end { hidesAppWindow(nonResizeableApp.defaultWindowName) } - visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(LAUNCHER_PACKAGE_NAME)) + visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, nonResizeableApp.defaultWindowName)) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt index 9586fd139eb5..bdc429c77661 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.legacysplitscreen +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -45,6 +46,7 @@ import org.junit.runners.Parameterized * Test open app to split screen. * To run this test: `atest WMShellFlickerTests:ExitLegacySplitScreenFromBottomTest` */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt index e9d3eb7f475d..26fabbd82fd5 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.legacysplitscreen +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.runWithFlicker @@ -34,6 +35,7 @@ import org.junit.runners.Parameterized * Test open app to split screen. * To run this test: `atest WMShellFlickerTests:NonResizableDismissInLegacySplitScreenTest` */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @@ -64,7 +66,8 @@ class NonResizableDismissInLegacySplitScreenTest( } visibleLayersShownMoreThanOneConsecutiveEntry( listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, - nonResizeableApp.defaultWindowName, LETTER_BOX_NAME) + nonResizeableApp.defaultWindowName, LETTER_BOX_NAME, + TOAST_NAME, LIVE_WALLPAPER_PACKAGE_NAME) ) } windowManagerTrace { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt index b5a36f5a31d4..e2439f216c84 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.legacysplitscreen +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.runWithFlicker @@ -34,6 +35,7 @@ import org.junit.runners.Parameterized * Test open app to split screen. * To run this test: `atest WMShellFlickerTests:NonResizableLaunchInLegacySplitScreenTest` */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @@ -44,7 +46,7 @@ class NonResizableLaunchInLegacySplitScreenTest( @Test fun testNonResizableLaunchInLegacySplitScreenTest() { - val testTag = "NonResizableLaunchInLegacySplitScreenTest" + val testTag = "testNonResizableLaunchInLegacySplitScreenTest" runWithFlicker(transitionSetup) { withTestName { testTag } @@ -64,7 +66,8 @@ class NonResizableLaunchInLegacySplitScreenTest( } visibleLayersShownMoreThanOneConsecutiveEntry( listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, - nonResizeableApp.defaultWindowName, LETTER_BOX_NAME) + nonResizeableApp.defaultWindowName, LETTER_BOX_NAME, + TOAST_NAME, LIVE_WALLPAPER_PACKAGE_NAME) ) } windowManagerTrace { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt index 90577ef19c1a..d004c0617b40 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt @@ -29,6 +29,7 @@ import com.android.server.wm.flicker.dsl.runWithFlicker import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.appPairsDividerBecomesVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder @@ -41,8 +42,7 @@ import org.junit.runners.Parameterized * Test open app to split screen. * To run this test: `atest WMShellFlickerTests:OpenAppToLegacySplitScreenTest` */ -// TODO: Add back to pre-submit when stable. -//@Presubmit +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @@ -53,28 +53,37 @@ class OpenAppToLegacySplitScreenTest( @Test fun OpenAppToLegacySplitScreenTest() { val testTag = "OpenAppToLegacySplitScreenTest" - + val helper = WindowManagerStateHelper() runWithFlicker(transitionSetup) { withTestName { testTag } repeat { SplitScreenHelper.TEST_REPETITIONS } + setup { + eachRun { + splitScreenApp.launchViaIntent() + device.pressHome() + this.setRotation(rotation) + } + } transitions { - splitScreenApp.launchViaIntent() - device.pressHome() - this.setRotation(rotation) device.launchSplitScreen() + helper.waitForAppTransitionIdle() } assertions { windowManagerTrace { - visibleWindowsShownMoreThanOneConsecutiveEntry() + visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + LETTER_BOX_NAME) + ) appWindowBecomesVisible(splitScreenApp.getPackage()) } layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) + navBarLayerIsAlwaysVisible() noUncoveredRegions(rotation, enabled = false) - statusBarLayerIsAlwaysVisible(bugId = 140855415) + statusBarLayerIsAlwaysVisible() visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME)) + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + LETTER_BOX_NAME)) appPairsDividerBecomesVisible() layerBecomesVisible(splitScreenApp.getPackage()) } @@ -92,7 +101,8 @@ class OpenAppToLegacySplitScreenTest( @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<Array<Any>> { - val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90) + // TODO(b/161435597) causes the test not to work on 90 degrees + val supportedRotations = intArrayOf(Surface.ROTATION_0) return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreenTest.kt index 391cb2a03d1c..8ee92637c024 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreenTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.legacysplitscreen +import android.platform.test.annotations.Presubmit import android.graphics.Region import android.util.Rational import android.view.Surface @@ -61,6 +62,7 @@ import org.junit.runners.Parameterized * * Currently it runs only in 0 degrees because of b/156100803 */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt index 923f2a4cd757..594b4c1cdb21 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.legacysplitscreen +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.FlickerBuilder @@ -42,6 +43,7 @@ import org.junit.runners.Parameterized * Test open app to split screen. * To run this test: `atest WMShellFlickerTests:RotateOneLaunchedAppTest` */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt index 4578f687689c..6ee0491a30dd 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.legacysplitscreen +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.FlickerBuilder @@ -43,6 +44,7 @@ import org.junit.runners.Parameterized * Test open app to split screen. * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppTest` */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt index 2b94c5f3fee9..8c9012455ef0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt @@ -52,6 +52,7 @@ abstract class SplitScreenTestBase( protected val LIVE_WALLPAPER_PACKAGE_NAME = "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2" protected val LETTER_BOX_NAME = "Letterbox" + protected val TOAST_NAME = "Toast" protected val transitionSetup: FlickerBuilder get() = FlickerBuilder(instrumentation).apply { 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 16d13f40f840..f141167178a1 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 @@ -28,7 +28,6 @@ import android.content.om.IOverlayManager; import android.os.Handler; import android.provider.Settings; import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; import android.view.Display; import androidx.test.filters.SmallTest; @@ -67,6 +66,8 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Mock OneHandedTimeoutHandler mMockTimeoutHandler; @Mock + OneHandedUiEventLogger mMockUiEventLogger; + @Mock IOverlayManager mMockOverlayManager; @Mock TaskStackListenerImpl mMockTaskStackListener; @@ -89,6 +90,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { mMockTutorialHandler, mMockGestureHandler, mTimeoutHandler, + mMockUiEventLogger, mMockOverlayManager, mMockTaskStackListener, mMockShellMainExecutor, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java index c451b8b2d2d7..c3e6bf376bda 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java @@ -21,7 +21,6 @@ import static org.mockito.Mockito.verify; import android.content.om.IOverlayManager; import android.os.Handler; import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; import androidx.test.filters.SmallTest; @@ -58,6 +57,9 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { ShellExecutor mMockShellMainExecutor; @Mock Handler mMockShellMainHandler; + @Mock + OneHandedUiEventLogger mMockUiEventLogger; + @Before public void setUp() { @@ -75,6 +77,7 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { mTutorialHandler, mGestureHandler, mTimeoutHandler, + mMockUiEventLogger, mMockOverlayManager, mMockTaskStackListener, mMockShellMainExecutor, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedEventsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedUiEventLoggerTest.java index 492c34e10ed5..e29fc6a91933 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedEventsTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedUiEventLoggerTest.java @@ -33,7 +33,7 @@ import java.util.Collection; @RunWith(Parameterized.class) @SmallTest -public class OneHandedEventsTest extends OneHandedTestCase { +public class OneHandedUiEventLoggerTest extends OneHandedTestCase { private UiEventLoggerFake mUiEventLogger; @@ -48,7 +48,6 @@ public class OneHandedEventsTest extends OneHandedTestCase { @Before public void setFakeLoggers() { mUiEventLogger = new UiEventLoggerFake(); - OneHandedEvents.sUiEventLogger = mUiEventLogger; } @Test @@ -63,42 +62,42 @@ public class OneHandedEventsTest extends OneHandedTestCase { public static Collection<Object[]> data() { return Arrays.asList(new Object[][]{ // Triggers - {OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN, + {OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN, "writeEvent one_handed_trigger_gesture_in"}, - {OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT, + {OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT, "writeEvent one_handed_trigger_gesture_out"}, - {OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_OVERSPACE_OUT, + {OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_OVERSPACE_OUT, "writeEvent one_handed_trigger_overspace_out"}, - {OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_POP_IME_OUT, + {OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_POP_IME_OUT, "writeEvent one_handed_trigger_pop_ime_out"}, - {OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_ROTATION_OUT, + {OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_ROTATION_OUT, "writeEvent one_handed_trigger_rotation_out"}, - {OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT, + {OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT, "writeEvent one_handed_trigger_app_taps_out"}, - {OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_TIMEOUT_OUT, + {OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_TIMEOUT_OUT, "writeEvent one_handed_trigger_timeout_out"}, - {OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_SCREEN_OFF_OUT, + {OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_SCREEN_OFF_OUT, "writeEvent one_handed_trigger_screen_off_out"}, // Settings toggles - {OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON, + {OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON, "writeEvent one_handed_settings_enabled_on"}, - {OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF, + {OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF, "writeEvent one_handed_settings_enabled_off"}, - {OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON, + {OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON, "writeEvent one_handed_settings_app_taps_exit_on"}, - {OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF, + {OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF, "writeEvent one_handed_settings_app_taps_exit_off"}, - {OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_EXIT_ON, + {OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_EXIT_ON, "writeEvent one_handed_settings_timeout_exit_on"}, - {OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_EXIT_OFF, + {OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_EXIT_OFF, "writeEvent one_handed_settings_timeout_exit_off"}, - {OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_NEVER, + {OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_NEVER, "writeEvent one_handed_settings_timeout_seconds_never"}, - {OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_4, + {OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_4, "writeEvent one_handed_settings_timeout_seconds_4"}, - {OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_8, + {OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_8, "writeEvent one_handed_settings_timeout_seconds_8"}, - {OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12, + {OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12, "writeEvent one_handed_settings_timeout_seconds_12"} }); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index c46e59ad396a..f3bee4ba27e8 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -16,15 +16,21 @@ package com.android.wm.shell.transition; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OPEN; +import static android.view.WindowManager.TRANSIT_TO_BACK; +import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -37,9 +43,14 @@ import static org.mockito.Mockito.verify; import android.app.ActivityManager.RunningTaskInfo; import android.os.Binder; import android.os.IBinder; +import android.os.RemoteException; +import android.view.IRemoteAnimationFinishedCallback; import android.view.SurfaceControl; import android.view.WindowManager; +import android.window.IRemoteTransition; +import android.window.TransitionFilter; import android.window.TransitionInfo; +import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; import android.window.WindowOrganizer; @@ -84,7 +95,8 @@ public class ShellTransitionTests { transitions.replaceDefaultHandlerForTest(mDefaultHandler); IBinder transitToken = new Binder(); - transitions.requestStartTransition(TRANSIT_OPEN, transitToken, null /* trigger */); + transitions.requestStartTransition(transitToken, + new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any()); TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); @@ -118,10 +130,10 @@ public class ShellTransitionTests { @Nullable @Override - public WindowContainerTransaction handleRequest(int type, @NonNull IBinder transition, - @Nullable RunningTaskInfo triggerTask) { - return (triggerTask != null - && triggerTask.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) + public WindowContainerTransaction handleRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request) { + final RunningTaskInfo task = request.getTriggerTask(); + return (task != null && task.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) ? handlerWCT : null; } }; @@ -132,7 +144,8 @@ public class ShellTransitionTests { .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); // Make a request that will be rejected by the testhandler. - transitions.requestStartTransition(TRANSIT_OPEN, transitToken, null /* trigger */); + transitions.requestStartTransition(transitToken, + new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), isNull()); transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class)); assertEquals(1, mDefaultHandler.activeCount()); @@ -143,7 +156,8 @@ public class ShellTransitionTests { // Make a request that will be handled by testhandler but not animated by it. RunningTaskInfo mwTaskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); - transitions.requestStartTransition(TRANSIT_OPEN, transitToken, mwTaskInfo); + transitions.requestStartTransition(transitToken, + new TransitionRequestInfo(TRANSIT_OPEN, mwTaskInfo, null /* remote */)); verify(mOrganizer, times(1)).startTransition( eq(TRANSIT_OPEN), eq(transitToken), eq(handlerWCT)); transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class)); @@ -157,9 +171,10 @@ public class ShellTransitionTests { // the test handler gets first shot at animating since it claimed to handle it. TestTransitionHandler topHandler = new TestTransitionHandler(); transitions.addHandler(topHandler); - transitions.requestStartTransition(TRANSIT_CHANGE, transitToken, mwTaskInfo); + transitions.requestStartTransition(transitToken, + new TransitionRequestInfo(TRANSIT_CHANGE, mwTaskInfo, null /* remote */)); verify(mOrganizer, times(1)).startTransition( - eq(TRANSIT_OPEN), eq(transitToken), eq(handlerWCT)); + eq(TRANSIT_CHANGE), eq(transitToken), eq(handlerWCT)); TransitionInfo change = new TransitionInfoBuilder(TRANSIT_CHANGE) .addChange(TRANSIT_CHANGE).build(); transitions.onTransitionReady(transitToken, change, mock(SurfaceControl.Transaction.class)); @@ -170,6 +185,110 @@ public class ShellTransitionTests { mMainExecutor.flushAll(); } + @Test + public void testRequestRemoteTransition() { + Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, + mAnimExecutor); + transitions.replaceDefaultHandlerForTest(mDefaultHandler); + + final boolean[] remoteCalled = new boolean[]{false}; + IRemoteTransition testRemote = new IRemoteTransition.Stub() { + @Override + public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t, + IRemoteAnimationFinishedCallback finishCallback) throws RemoteException { + remoteCalled[0] = true; + finishCallback.onAnimationFinished(); + } + }; + IBinder transitToken = new Binder(); + transitions.requestStartTransition(transitToken, + new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, testRemote)); + verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any()); + TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) + .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); + transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class)); + assertEquals(0, mDefaultHandler.activeCount()); + assertTrue(remoteCalled[0]); + mDefaultHandler.finishAll(); + mMainExecutor.flushAll(); + verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any(), any()); + } + + @Test + public void testTransitionFilterActivityType() { + TransitionFilter filter = new TransitionFilter(); + filter.mRequirements = + new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()}; + filter.mRequirements[0].mActivityType = ACTIVITY_TYPE_HOME; + filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; + + final TransitionInfo openHome = new TransitionInfoBuilder(TRANSIT_OPEN) + .addChange(TRANSIT_OPEN, + createTaskInfo(1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME)).build(); + assertTrue(filter.matches(openHome)); + + final TransitionInfo openStd = new TransitionInfoBuilder(TRANSIT_OPEN) + .addChange(TRANSIT_OPEN, createTaskInfo( + 1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD)).build(); + assertFalse(filter.matches(openStd)); + } + + @Test + public void testTransitionFilterMultiRequirement() { + // filter that requires at-least one opening and one closing app + TransitionFilter filter = new TransitionFilter(); + filter.mRequirements = new TransitionFilter.Requirement[]{ + new TransitionFilter.Requirement(), new TransitionFilter.Requirement()}; + filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; + filter.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK}; + + final TransitionInfo openOnly = new TransitionInfoBuilder(TRANSIT_OPEN) + .addChange(TRANSIT_OPEN).build(); + assertFalse(filter.matches(openOnly)); + + final TransitionInfo openClose = new TransitionInfoBuilder(TRANSIT_OPEN) + .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); + assertTrue(filter.matches(openClose)); + } + + @Test + public void testRegisteredRemoteTransition() { + Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, + mAnimExecutor); + transitions.replaceDefaultHandlerForTest(mDefaultHandler); + + final boolean[] remoteCalled = new boolean[]{false}; + IRemoteTransition testRemote = new IRemoteTransition.Stub() { + @Override + public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t, + IRemoteAnimationFinishedCallback finishCallback) throws RemoteException { + remoteCalled[0] = true; + finishCallback.onAnimationFinished(); + } + }; + + TransitionFilter filter = new TransitionFilter(); + filter.mRequirements = + new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()}; + filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; + + transitions.registerRemote(filter, testRemote); + mMainExecutor.flushAll(); + + IBinder transitToken = new Binder(); + transitions.requestStartTransition(transitToken, + new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); + verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any()); + TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) + .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); + transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class)); + assertEquals(0, mDefaultHandler.activeCount()); + assertTrue(remoteCalled[0]); + mDefaultHandler.finishAll(); + mMainExecutor.flushAll(); + verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any(), any()); + } + class TransitionInfoBuilder { final TransitionInfo mInfo; @@ -209,8 +328,8 @@ public class ShellTransitionTests { @Nullable @Override - public WindowContainerTransaction handleRequest(int type, @NonNull IBinder transition, - @Nullable RunningTaskInfo triggerTask) { + public WindowContainerTransaction handleRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request) { return null; } @@ -240,4 +359,10 @@ public class ShellTransitionTests { return taskInfo; } + private static RunningTaskInfo createTaskInfo(int taskId) { + RunningTaskInfo taskInfo = new RunningTaskInfo(); + taskInfo.taskId = taskId; + return taskInfo; + } + } diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 6bf2e9963777..11a908696903 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -16,12 +16,14 @@ #include "RecordingCanvas.h" -#include "pipeline/skia/FunctorDrawable.h" -#include "VectorDrawable.h" +#include <GrRecordingContext.h> + +#include <experimental/type_traits> #include "SkAndroidFrameworkUtils.h" #include "SkCanvas.h" #include "SkCanvasPriv.h" +#include "SkColor.h" #include "SkData.h" #include "SkDrawShadowInfo.h" #include "SkImage.h" @@ -33,8 +35,8 @@ #include "SkRegion.h" #include "SkTextBlob.h" #include "SkVertices.h" - -#include <experimental/type_traits> +#include "VectorDrawable.h" +#include "pipeline/skia/FunctorDrawable.h" namespace android { namespace uirenderer { @@ -500,7 +502,68 @@ struct DrawWebView final : Op { // SkDrawable::onSnapGpuDrawHandler callback instead of SkDrawable::onDraw. // SkCanvas::drawDrawable/SkGpuDevice::drawDrawable has the logic to invoke // onSnapGpuDrawHandler. - void draw(SkCanvas* c, const SkMatrix&) const { c->drawDrawable(drawable.get()); } +private: + // Unfortunately WebView does not have complex clip information serialized, and we only perform + // best-effort stencil fill for GLES. So for Vulkan we create an intermediate layer if the + // canvas clip is complex. + static bool needsCompositedLayer(SkCanvas* c) { + if (Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan) { + return false; + } + SkRegion clipRegion; + // WebView's rasterizer has access to simple clips, so for Vulkan we only need to check if + // the clip is more complex than a rectangle. + c->temporary_internal_getRgnClip(&clipRegion); + return clipRegion.isComplex(); + } + + mutable SkImageInfo mLayerImageInfo; + mutable sk_sp<SkSurface> mLayerSurface = nullptr; + +public: + void draw(SkCanvas* c, const SkMatrix&) const { + if (needsCompositedLayer(c)) { + // What we do now is create an offscreen surface, sized by the clip bounds. + // We won't apply a clip while drawing - clipping will be performed when compositing the + // surface back onto the original canvas. Note also that we're not using saveLayer + // because the webview functor still doesn't respect the canvas clip stack. + const SkIRect deviceBounds = c->getDeviceClipBounds(); + if (mLayerSurface == nullptr || c->imageInfo() != mLayerImageInfo) { + GrRecordingContext* directContext = c->recordingContext(); + mLayerImageInfo = + c->imageInfo().makeWH(deviceBounds.width(), deviceBounds.height()); + mLayerSurface = SkSurface::MakeRenderTarget(directContext, SkBudgeted::kYes, + mLayerImageInfo, 0, + kTopLeft_GrSurfaceOrigin, nullptr); + } + + SkCanvas* layerCanvas = mLayerSurface->getCanvas(); + + SkAutoCanvasRestore(layerCanvas, true); + layerCanvas->clear(SK_ColorTRANSPARENT); + + // Preserve the transform from the original canvas, but now the clip rectangle is + // anchored at the origin so we need to transform the clipped content to the origin. + SkM44 mat4(c->getLocalToDevice()); + mat4.postTranslate(-deviceBounds.fLeft, -deviceBounds.fTop); + layerCanvas->concat(mat4); + layerCanvas->drawDrawable(drawable.get()); + + SkAutoCanvasRestore acr(c, true); + + // Temporarily use an identity transform, because this is just blitting to the parent + // canvas with an offset. + SkMatrix invertedMatrix; + if (!c->getTotalMatrix().invert(&invertedMatrix)) { + ALOGW("Unable to extract invert canvas matrix; aborting VkFunctor draw"); + return; + } + c->concat(invertedMatrix); + mLayerSurface->draw(c, deviceBounds.fLeft, deviceBounds.fTop, nullptr); + } else { + c->drawDrawable(drawable.get()); + } + } }; } diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 815ffdeade45..1ebc48900349 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -819,10 +819,10 @@ void SkiaCanvas::drawRipple(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint, uirenderer::CanvasPropertyPrimitive* progress, - sk_sp<SkRuntimeEffect> runtimeEffect) { + const SkRuntimeShaderBuilder& effectBuilder) { sk_sp<uirenderer::skiapipeline::AnimatedRipple> drawable( new uirenderer::skiapipeline::AnimatedRipple(x, y, radius, paint, progress, - runtimeEffect)); + effectBuilder)); mCanvas->drawDrawable(drawable.get()); } diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index b9370e942463..155f6df7b703 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -151,7 +151,7 @@ public: uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint, uirenderer::CanvasPropertyPrimitive* progress, - sk_sp<SkRuntimeEffect> runtimeEffect) override; + const SkRuntimeShaderBuilder& effectBuilder) override; virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override; virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override; diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h index ea9fea974d06..fa0c45b5221c 100644 --- a/libs/hwui/canvas/CanvasOps.h +++ b/libs/hwui/canvas/CanvasOps.h @@ -161,7 +161,7 @@ struct CanvasOp<CanvasOpType::DrawRippleProperty> { } SkRuntimeShaderBuilder::BuilderUniform radiusU = - runtimeEffectBuilder.uniform("in_maxRadius"); + runtimeEffectBuilder.uniform("in_radius"); if (radiusU.fVar != nullptr) { radiusU = radius->value; } diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index 19ef7e377deb..fdfa2883c33f 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -30,7 +30,7 @@ class SkAnimatedImage; class SkCanvasState; -class SkRuntimeEffect; +class SkRuntimeShaderBuilder; class SkVertices; namespace minikin { @@ -138,7 +138,7 @@ public: uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint, uirenderer::CanvasPropertyPrimitive* progress, - sk_sp<SkRuntimeEffect> runtimeEffect) = 0; + const SkRuntimeShaderBuilder& effectBuilder) = 0; virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) = 0; virtual void drawRenderNode(uirenderer::RenderNode* renderNode) = 0; diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp index 764bc4c7ff0a..f055c6e0fa44 100644 --- a/libs/hwui/hwui/ImageDecoder.cpp +++ b/libs/hwui/hwui/ImageDecoder.cpp @@ -76,6 +76,11 @@ static bool requires_matrix_scaling(bool swapWidthHeight, const SkISize& decodeS || (!swapWidthHeight && decodeSize != targetSize); } +SkISize ImageDecoder::getSampledDimensions(int sampleSize) const { + auto size = mCodec->getSampledDimensions(sampleSize); + return swapWidthHeight() ? swapped(size) : size; +} + bool ImageDecoder::setTargetSize(int width, int height) { if (width <= 0 || height <= 0) { return false; diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h index 1b309bcc7bf0..cbfffd5e9291 100644 --- a/libs/hwui/hwui/ImageDecoder.h +++ b/libs/hwui/hwui/ImageDecoder.h @@ -38,6 +38,7 @@ public: sk_sp<SkPngChunkReader> peeker = nullptr); ~ImageDecoder(); + SkISize getSampledDimensions(int sampleSize) const; bool setTargetSize(int width, int height); bool setCropRect(const SkIRect*); diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp index 96e912fd9f26..ad7741b61e9f 100644 --- a/libs/hwui/jni/ImageDecoder.cpp +++ b/libs/hwui/jni/ImageDecoder.cpp @@ -465,7 +465,7 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong static jobject ImageDecoder_nGetSampledSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, jint sampleSize) { auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr); - SkISize size = decoder->mCodec->getSampledDimensions(sampleSize); + SkISize size = decoder->getSampledDimensions(sampleSize); return env->NewObject(gSize_class, gSize_constructorMethodID, size.width(), size.height()); } diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp index aaec60baaa4a..1dc5cd99eed1 100644 --- a/libs/hwui/jni/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -1,3 +1,6 @@ +#undef LOG_TAG +#define LOG_TAG "ShaderJNI" + #include "GraphicsJNI.h" #include "SkColorFilter.h" #include "SkGradientShader.h" @@ -232,53 +235,73 @@ static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr, /////////////////////////////////////////////////////////////////////////////////////////////// -static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr, - jbyteArray inputs, jlongArray inputShaders, jlong colorSpaceHandle, jboolean isOpaque) { - SkRuntimeEffect* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory); - AutoJavaByteArray arInputs(env, inputs); - - std::vector<sk_sp<SkShader>> shaderVector; - if (inputShaders) { - jsize shaderCount = env->GetArrayLength(inputShaders); - shaderVector.resize(shaderCount); - jlong* arrayPtr = env->GetLongArrayElements(inputShaders, NULL); - for (int i = 0; i < shaderCount; i++) { - shaderVector[i] = sk_ref_sp(reinterpret_cast<SkShader*>(arrayPtr[i])); - } - env->ReleaseLongArrayElements(inputShaders, arrayPtr, 0); - } - - sk_sp<SkData> fData; - fData = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length()); - const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - sk_sp<SkShader> shader = effect->makeShader(fData, shaderVector.data(), shaderVector.size(), - matrix, isOpaque == JNI_TRUE); - ThrowIAE_IfNull(env, shader); - - return reinterpret_cast<jlong>(shader.release()); -} - /////////////////////////////////////////////////////////////////////////////////////////////// -static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sksl) { +static jlong RuntimeShader_createShaderBuilder(JNIEnv* env, jobject, jstring sksl) { ScopedUtfChars strSksl(env, sksl); auto result = SkRuntimeEffect::Make(SkString(strSksl.c_str())); sk_sp<SkRuntimeEffect> effect = std::get<0>(result); - if (!effect) { + if (effect.get() == nullptr) { const auto& err = std::get<1>(result); doThrowIAE(env, err.c_str()); + return 0; } - return reinterpret_cast<jlong>(effect.release()); + return reinterpret_cast<jlong>(new SkRuntimeShaderBuilder(std::move(effect))); } -/////////////////////////////////////////////////////////////////////////////////////////////// - -static void Effect_safeUnref(SkRuntimeEffect* effect) { - SkSafeUnref(effect); +static void SkRuntimeShaderBuilder_delete(SkRuntimeShaderBuilder* builder) { + delete builder; } static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) { - return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Effect_safeUnref)); + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&SkRuntimeShaderBuilder_delete)); +} + +static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderBuilder, jlong matrixPtr, + jboolean isOpaque) { + SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder); + const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); + sk_sp<SkShader> shader = builder->makeShader(matrix, isOpaque == JNI_TRUE); + ThrowIAE_IfNull(env, shader); + return reinterpret_cast<jlong>(shader.release()); +} + +static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args); + va_end(args); + return ret; +} + +static void RuntimeShader_updateUniforms(JNIEnv* env, jobject, jlong shaderBuilder, + jstring jUniformName, jfloatArray jvalues) { + SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder); + ScopedUtfChars name(env, jUniformName); + AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess); + + SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(name.c_str()); + if (uniform.fVar == nullptr) { + ThrowIAEFmt(env, "unable to find uniform named %s", name.c_str()); + } else if (!uniform.set<float>(autoValues.ptr(), autoValues.length())) { + ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]", + uniform.fVar->sizeInBytes(), sizeof(float) * autoValues.length()); + } +} + +static void RuntimeShader_updateShader(JNIEnv* env, jobject, jlong shaderBuilder, + jstring jUniformName, jlong shaderHandle) { + SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder); + ScopedUtfChars name(env, jUniformName); + SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle); + + SkRuntimeShaderBuilder::BuilderChild child = builder->child(name.c_str()); + if (child.fIndex == -1) { + ThrowIAEFmt(env, "unable to find shader named %s", name.c_str()); + return; + } + + builder->child(name.c_str()) = sk_ref_sp(shader); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -313,10 +336,11 @@ static const JNINativeMethod gComposeShaderMethods[] = { }; static const JNINativeMethod gRuntimeShaderMethods[] = { - { "nativeGetFinalizer", "()J", (void*)RuntimeShader_getNativeFinalizer }, - { "nativeCreate", "(JJ[B[JJZ)J", (void*)RuntimeShader_create }, - { "nativeCreateShaderFactory", "(Ljava/lang/String;)J", - (void*)RuntimeShader_createShaderFactory }, + {"nativeGetFinalizer", "()J", (void*)RuntimeShader_getNativeFinalizer}, + {"nativeCreateShader", "(JJZ)J", (void*)RuntimeShader_create}, + {"nativeCreateBuilder", "(Ljava/lang/String;)J", (void*)RuntimeShader_createShaderBuilder}, + {"nativeUpdateUniforms", "(JLjava/lang/String;[F)V", (void*)RuntimeShader_updateUniforms}, + {"nativeUpdateShader", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateShader}, }; int register_android_graphics_Shader(JNIEnv* env) diff --git a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp index 360492dc36c6..855d56ee2e55 100644 --- a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp +++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp @@ -144,7 +144,8 @@ static void android_view_DisplayListCanvas_drawCircleProps(CRITICAL_JNI_PARAMS_C static void android_view_DisplayListCanvas_drawRippleProps(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jlong xPropPtr, jlong yPropPtr, jlong radiusPropPtr, jlong paintPropPtr, - jlong progressPropPtr, jlong effectPtr) { + jlong progressPropPtr, + jlong builderPtr) { Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); CanvasPropertyPrimitive* xProp = reinterpret_cast<CanvasPropertyPrimitive*>(xPropPtr); CanvasPropertyPrimitive* yProp = reinterpret_cast<CanvasPropertyPrimitive*>(yPropPtr); @@ -152,8 +153,8 @@ static void android_view_DisplayListCanvas_drawRippleProps(CRITICAL_JNI_PARAMS_C CanvasPropertyPaint* paintProp = reinterpret_cast<CanvasPropertyPaint*>(paintPropPtr); CanvasPropertyPrimitive* progressProp = reinterpret_cast<CanvasPropertyPrimitive*>(progressPropPtr); - SkRuntimeEffect* effect = reinterpret_cast<SkRuntimeEffect*>(effectPtr); - canvas->drawRipple(xProp, yProp, radiusProp, paintProp, progressProp, sk_ref_sp(effect)); + SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(builderPtr); + canvas->drawRipple(xProp, yProp, radiusProp, paintProp, progressProp, *builder); } static void android_view_DisplayListCanvas_drawWebViewFunctor(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jint functor) { diff --git a/libs/hwui/pipeline/skia/AnimatedDrawables.h b/libs/hwui/pipeline/skia/AnimatedDrawables.h index 3142d927d4c4..78591450f10a 100644 --- a/libs/hwui/pipeline/skia/AnimatedDrawables.h +++ b/libs/hwui/pipeline/skia/AnimatedDrawables.h @@ -61,13 +61,13 @@ public: uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint, uirenderer::CanvasPropertyPrimitive* progress, - sk_sp<SkRuntimeEffect> runtimeEffect) + const SkRuntimeShaderBuilder& effectBuilder) : mX(x) , mY(y) , mRadius(radius) , mPaint(paint) , mProgress(progress) - , mRuntimeEffectBuilder(std::move(runtimeEffect)) {} + , mRuntimeEffectBuilder(effectBuilder) {} protected: virtual SkRect onGetBounds() override { @@ -83,7 +83,7 @@ protected: } SkRuntimeShaderBuilder::BuilderUniform radiusU = - mRuntimeEffectBuilder.uniform("in_maxRadius"); + mRuntimeEffectBuilder.uniform("in_radius"); if (radiusU.fVar != nullptr) { radiusU = mRadius->value; } diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index e6c6e1094c40..3498f715b455 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -102,12 +102,12 @@ bool SkiaDisplayList::prepareListAndChildren( bool hasBackwardProjectedNodesSubtree = false; for (auto& child : mChildNodes) { - hasBackwardProjectedNodesHere |= child.getNodeProperties().getProjectBackwards(); RenderNode* childNode = child.getRenderNode(); Matrix4 mat4(child.getRecordedMatrix()); info.damageAccumulator->pushTransform(&mat4); info.hasBackwardProjectedNodes = false; childFn(childNode, observer, info, functorsNeedLayer); + hasBackwardProjectedNodesHere |= child.getNodeProperties().getProjectBackwards(); hasBackwardProjectedNodesSubtree |= info.hasBackwardProjectedNodes; info.damageAccumulator->popTransform(); } diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 0b4bb75d6c3d..ee7c4d8bb54a 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -94,9 +94,9 @@ void SkiaRecordingCanvas::drawRipple(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint, uirenderer::CanvasPropertyPrimitive* progress, - sk_sp<SkRuntimeEffect> runtimeEffect) { + const SkRuntimeShaderBuilder& effectBuilder) { drawDrawable(mDisplayList->allocateDrawable<AnimatedRipple>(x, y, radius, paint, progress, - runtimeEffect)); + effectBuilder)); } void SkiaRecordingCanvas::enableZ(bool enableZ) { @@ -308,10 +308,13 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& ch SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); sk_sp<SkImage> image = bitmap.makeImage(); + // HWUI always draws 9-patches with linear filtering, regardless of the Paint. + const SkFilterMode filter = SkFilterMode::kLinear; + applyLooper(get_looper(paint), filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint* p) { - mRecorder.drawImageLattice(image, lattice, dst.makeOffset(x, y), Paint_to_filter(p), - p, bitmap.palette()); + mRecorder.drawImageLattice(image, lattice, dst.makeOffset(x, y), filter, p, + bitmap.palette()); }); if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) { diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index 0b543aef3cf2..8d7a21a732dd 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -72,7 +72,7 @@ public: uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint, uirenderer::CanvasPropertyPrimitive* progress, - sk_sp<SkRuntimeEffect> runtimeEffect) override; + const SkRuntimeShaderBuilder& effectBuilder) override; virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override; diff --git a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl index a1b13ba31bef..e55678d90078 100644 --- a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl +++ b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl @@ -19,6 +19,8 @@ package android.media.metrics; import android.media.metrics.NetworkEvent; import android.media.metrics.PlaybackErrorEvent; import android.media.metrics.PlaybackMetrics; +import android.media.metrics.PlaybackStateEvent; +import android.media.metrics.TrackChangeEvent; /** * Interface to the playback manager service. @@ -27,6 +29,8 @@ import android.media.metrics.PlaybackMetrics; interface IPlaybackMetricsManager { void reportPlaybackMetrics(in String sessionId, in PlaybackMetrics metrics, int userId); String getSessionId(int userId); - void reportPlaybackErrorEvent(in String sessionId, in PlaybackErrorEvent event, int userId); void reportNetworkEvent(in String sessionId, in NetworkEvent event, int userId); + void reportPlaybackErrorEvent(in String sessionId, in PlaybackErrorEvent event, int userId); + void reportPlaybackStateEvent(in String sessionId, in PlaybackStateEvent event, int userId); + void reportTrackChangeEvent(in String sessionId, in TrackChangeEvent event, int userId); }
\ No newline at end of file diff --git a/media/java/android/media/metrics/PlaybackMetrics.java b/media/java/android/media/metrics/PlaybackMetrics.java index 82a58036cfa4..070b4e4aa14b 100644 --- a/media/java/android/media/metrics/PlaybackMetrics.java +++ b/media/java/android/media/metrics/PlaybackMetrics.java @@ -16,11 +16,19 @@ package android.media.metrics; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.util.AnnotationValidations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Objects; /** @@ -28,38 +36,323 @@ import java.util.Objects; * @hide */ public final class PlaybackMetrics implements Parcelable { - private int mStreamSourceType; + // TODO(b/177209128): JavaDoc for the constants. + public static final int STREAM_SOURCE_UNKNOWN = 0; + public static final int STREAM_SOURCE_NETWORK = 1; + public static final int STREAM_SOURCE_DEVICE = 2; + public static final int STREAM_SOURCE_MIXED = 3; + + public static final int STREAM_TYPE_UNKNOWN = 0; + public static final int STREAM_TYPE_OTHER = 1; + public static final int STREAM_TYPE_PROGRESSIVE = 2; + public static final int STREAM_TYPE_DASH = 3; + public static final int STREAM_TYPE_HLS = 4; + public static final int STREAM_TYPE_SS = 5; + + public static final int PLAYBACK_TYPE_VOD = 0; + public static final int PLAYBACK_TYPE_LIVE = 1; + public static final int PLAYBACK_TYPE_OTHER = 2; + + public static final int DRM_TYPE_NONE = 0; + public static final int DRM_TYPE_OTHER = 1; + public static final int DRM_TYPE_PLAY_READY = 2; + public static final int DRM_TYPE_WIDEVINE_L1 = 3; + public static final int DRM_TYPE_WIDEVINE_L3 = 4; + // TODO: add DRM_TYPE_CLEARKEY + + public static final int CONTENT_TYPE_MAIN = 0; + public static final int CONTENT_TYPE_AD = 1; + public static final int CONTENT_TYPE_OTHER = 2; + + + /** @hide */ + @IntDef(prefix = "STREAM_SOURCE_", value = { + STREAM_SOURCE_UNKNOWN, + STREAM_SOURCE_NETWORK, + STREAM_SOURCE_DEVICE, + STREAM_SOURCE_MIXED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface StreamSource {} + + /** @hide */ + @IntDef(prefix = "STREAM_TYPE_", value = { + STREAM_TYPE_UNKNOWN, + STREAM_TYPE_OTHER, + STREAM_TYPE_PROGRESSIVE, + STREAM_TYPE_DASH, + STREAM_TYPE_HLS, + STREAM_TYPE_SS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface StreamType {} + + /** @hide */ + @IntDef(prefix = "PLAYBACK_TYPE_", value = { + PLAYBACK_TYPE_VOD, + PLAYBACK_TYPE_LIVE, + PLAYBACK_TYPE_OTHER + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PlaybackType {} + + /** @hide */ + @IntDef(prefix = "DRM_TYPE_", value = { + DRM_TYPE_NONE, + DRM_TYPE_OTHER, + DRM_TYPE_PLAY_READY, + DRM_TYPE_WIDEVINE_L1, + DRM_TYPE_WIDEVINE_L3 + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DrmType {} + + /** @hide */ + @IntDef(prefix = "CONTENT_TYPE_", value = { + CONTENT_TYPE_MAIN, + CONTENT_TYPE_AD, + CONTENT_TYPE_OTHER + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ContentType {} + + + + private final long mMediaDurationMillis; + private final int mStreamSource; + private final int mStreamType; + private final int mPlaybackType; + private final int mDrmType; + private final int mContentType; + private final @Nullable String mPlayerName; + private final @Nullable String mPlayerVersion; + private final @NonNull long[] mExperimentIds; + private final int mVideoFramesPlayed; + private final int mVideoFramesDropped; + private final int mAudioUnderrunCount; + private final long mNetworkBytesRead; + private final long mLocalBytesRead; + private final long mNetworkTransferDurationMillis; /** * Creates a new PlaybackMetrics. * * @hide */ - public PlaybackMetrics(int streamSourceType) { - this.mStreamSourceType = streamSourceType; + public PlaybackMetrics( + long mediaDurationMillis, + int streamSource, + int streamType, + int playbackType, + int drmType, + int contentType, + @Nullable String playerName, + @Nullable String playerVersion, + @NonNull long[] experimentIds, + int videoFramesPlayed, + int videoFramesDropped, + int audioUnderrunCount, + long networkBytesRead, + long localBytesRead, + long networkTransferDurationMillis) { + this.mMediaDurationMillis = mediaDurationMillis; + this.mStreamSource = streamSource; + this.mStreamType = streamType; + this.mPlaybackType = playbackType; + this.mDrmType = drmType; + this.mContentType = contentType; + this.mPlayerName = playerName; + this.mPlayerVersion = playerVersion; + this.mExperimentIds = experimentIds; + AnnotationValidations.validate(NonNull.class, null, mExperimentIds); + this.mVideoFramesPlayed = videoFramesPlayed; + this.mVideoFramesDropped = videoFramesDropped; + this.mAudioUnderrunCount = audioUnderrunCount; + this.mNetworkBytesRead = networkBytesRead; + this.mLocalBytesRead = localBytesRead; + this.mNetworkTransferDurationMillis = networkTransferDurationMillis; + } + + public long getMediaDurationMillis() { + return mMediaDurationMillis; + } + + /** + * Gets stream source type. + */ + @StreamSource + public int getStreamSource() { + return mStreamSource; + } + + /** + * Gets stream type. + */ + @StreamType + public int getStreamType() { + return mStreamType; + } + + + /** + * Gets playback type. + */ + @PlaybackType + public int getPlaybackType() { + return mPlaybackType; + } + + /** + * Gets DRM type. + */ + @DrmType + public int getDrmType() { + return mDrmType; + } + + /** + * Gets content type. + */ + @ContentType + public int getContentType() { + return mContentType; + } + + /** + * Gets player name. + */ + public @Nullable String getPlayerName() { + return mPlayerName; + } + + /** + * Gets player version. + */ + public @Nullable String getPlayerVersion() { + return mPlayerVersion; + } + + /** + * Gets experiment IDs. + */ + public @NonNull long[] getExperimentIds() { + return Arrays.copyOf(mExperimentIds, mExperimentIds.length); + } + + /** + * Gets video frames played. + */ + public int getVideoFramesPlayed() { + return mVideoFramesPlayed; + } + + /** + * Gets video frames dropped. + */ + public int getVideoFramesDropped() { + return mVideoFramesDropped; } - public int getStreamSourceType() { - return mStreamSourceType; + /** + * Gets audio underrun count. + */ + public int getAudioUnderrunCount() { + return mAudioUnderrunCount; + } + + /** + * Gets number of network bytes read. + */ + public long getNetworkBytesRead() { + return mNetworkBytesRead; + } + + /** + * Gets number of local bytes read. + */ + public long getLocalBytesRead() { + return mLocalBytesRead; + } + + /** + * Gets network transfer duration in milliseconds. + */ + public long getNetworkTransferDurationMillis() { + return mNetworkTransferDurationMillis; } @Override - public boolean equals(@Nullable Object o) { + public String toString() { + return "PlaybackMetrics { " + + "mediaDurationMillis = " + mMediaDurationMillis + ", " + + "streamSource = " + mStreamSource + ", " + + "streamType = " + mStreamType + ", " + + "playbackType = " + mPlaybackType + ", " + + "drmType = " + mDrmType + ", " + + "contentType = " + mContentType + ", " + + "playerName = " + mPlayerName + ", " + + "playerVersion = " + mPlayerVersion + ", " + + "experimentIds = " + Arrays.toString(mExperimentIds) + ", " + + "videoFramesPlayed = " + mVideoFramesPlayed + ", " + + "videoFramesDropped = " + mVideoFramesDropped + ", " + + "audioUnderrunCount = " + mAudioUnderrunCount + ", " + + "networkBytesRead = " + mNetworkBytesRead + ", " + + "localBytesRead = " + mLocalBytesRead + ", " + + "networkTransferDurationMillis = " + mNetworkTransferDurationMillis + + " }"; + } + @Override + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PlaybackMetrics that = (PlaybackMetrics) o; - return mStreamSourceType == that.mStreamSourceType; + return mMediaDurationMillis == that.mMediaDurationMillis + && mStreamSource == that.mStreamSource + && mStreamType == that.mStreamType + && mPlaybackType == that.mPlaybackType + && mDrmType == that.mDrmType + && mContentType == that.mContentType + && Objects.equals(mPlayerName, that.mPlayerName) + && Objects.equals(mPlayerVersion, that.mPlayerVersion) + && Arrays.equals(mExperimentIds, that.mExperimentIds) + && mVideoFramesPlayed == that.mVideoFramesPlayed + && mVideoFramesDropped == that.mVideoFramesDropped + && mAudioUnderrunCount == that.mAudioUnderrunCount + && mNetworkBytesRead == that.mNetworkBytesRead + && mLocalBytesRead == that.mLocalBytesRead + && mNetworkTransferDurationMillis == that.mNetworkTransferDurationMillis; } @Override public int hashCode() { - return Objects.hash(mStreamSourceType); + return Objects.hash(mMediaDurationMillis, mStreamSource, mStreamType, mPlaybackType, + mDrmType, mContentType, mPlayerName, mPlayerVersion, mExperimentIds, + mVideoFramesPlayed, mVideoFramesDropped, mAudioUnderrunCount, mNetworkBytesRead, + mLocalBytesRead, mNetworkTransferDurationMillis); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mStreamSourceType); + long flg = 0; + if (mPlayerName != null) flg |= 0x80; + if (mPlayerVersion != null) flg |= 0x100; + dest.writeLong(flg); + dest.writeLong(mMediaDurationMillis); + dest.writeInt(mStreamSource); + dest.writeInt(mStreamType); + dest.writeInt(mPlaybackType); + dest.writeInt(mDrmType); + dest.writeInt(mContentType); + if (mPlayerName != null) dest.writeString(mPlayerName); + if (mPlayerVersion != null) dest.writeString(mPlayerVersion); + dest.writeLongArray(mExperimentIds); + dest.writeInt(mVideoFramesPlayed); + dest.writeInt(mVideoFramesDropped); + dest.writeInt(mAudioUnderrunCount); + dest.writeLong(mNetworkBytesRead); + dest.writeLong(mLocalBytesRead); + dest.writeLong(mNetworkTransferDurationMillis); } @Override @@ -69,20 +362,231 @@ public final class PlaybackMetrics implements Parcelable { /** @hide */ /* package-private */ PlaybackMetrics(@NonNull Parcel in) { - int streamSourceType = in.readInt(); - this.mStreamSourceType = streamSourceType; + long flg = in.readLong(); + long mediaDurationMillis = in.readLong(); + int streamSource = in.readInt(); + int streamType = in.readInt(); + int playbackType = in.readInt(); + int drmType = in.readInt(); + int contentType = in.readInt(); + String playerName = (flg & 0x80) == 0 ? null : in.readString(); + String playerVersion = (flg & 0x100) == 0 ? null : in.readString(); + long[] experimentIds = in.createLongArray(); + int videoFramesPlayed = in.readInt(); + int videoFramesDropped = in.readInt(); + int audioUnderrunCount = in.readInt(); + long networkBytesRead = in.readLong(); + long localBytesRead = in.readLong(); + long networkTransferDurationMillis = in.readLong(); + + this.mMediaDurationMillis = mediaDurationMillis; + this.mStreamSource = streamSource; + this.mStreamType = streamType; + this.mPlaybackType = playbackType; + this.mDrmType = drmType; + this.mContentType = contentType; + this.mPlayerName = playerName; + this.mPlayerVersion = playerVersion; + this.mExperimentIds = experimentIds; + AnnotationValidations.validate(NonNull.class, null, mExperimentIds); + this.mVideoFramesPlayed = videoFramesPlayed; + this.mVideoFramesDropped = videoFramesDropped; + this.mAudioUnderrunCount = audioUnderrunCount; + this.mNetworkBytesRead = networkBytesRead; + this.mLocalBytesRead = localBytesRead; + this.mNetworkTransferDurationMillis = networkTransferDurationMillis; } public static final @NonNull Parcelable.Creator<PlaybackMetrics> CREATOR = new Parcelable.Creator<PlaybackMetrics>() { - @Override - public PlaybackMetrics[] newArray(int size) { - return new PlaybackMetrics[size]; - } - - @Override - public PlaybackMetrics createFromParcel(@NonNull Parcel in) { - return new PlaybackMetrics(in); - } - }; + @Override + public PlaybackMetrics[] newArray(int size) { + return new PlaybackMetrics[size]; + } + + @Override + public PlaybackMetrics createFromParcel(@NonNull Parcel in) { + return new PlaybackMetrics(in); + } + }; + + /** + * A builder for {@link PlaybackMetrics} + */ + public static final class Builder { + + private long mMediaDurationMillis; + private int mStreamSource; + private int mStreamType; + private int mPlaybackType; + private int mDrmType; + private int mContentType; + private @Nullable String mPlayerName; + private @Nullable String mPlayerVersion; + private @NonNull List<Long> mExperimentIds = new ArrayList<>(); + private int mVideoFramesPlayed; + private int mVideoFramesDropped; + private int mAudioUnderrunCount; + private long mNetworkBytesRead; + private long mLocalBytesRead; + private long mNetworkTransferDurationMillis; + + /** + * Creates a new Builder. + * + * @hide + */ + public Builder() { + } + + /** + * Sets the media duration in milliseconds. + */ + public @NonNull Builder setMediaDurationMillis(long value) { + mMediaDurationMillis = value; + return this; + } + + /** + * Sets the stream source type. + */ + public @NonNull Builder setStreamSource(@StreamSource int value) { + mStreamSource = value; + return this; + } + + /** + * Sets the stream type. + */ + public @NonNull Builder setStreamType(@StreamType int value) { + mStreamType = value; + return this; + } + + /** + * Sets the playback type. + */ + public @NonNull Builder setPlaybackType(@PlaybackType int value) { + mPlaybackType = value; + return this; + } + + /** + * Sets the DRM type. + */ + public @NonNull Builder setDrmType(@StreamType int value) { + mDrmType = value; + return this; + } + + /** + * Sets the content type. + */ + public @NonNull Builder setContentType(@ContentType int value) { + mContentType = value; + return this; + } + + /** + * Sets the player name. + */ + public @NonNull Builder setPlayerName(@NonNull String value) { + mPlayerName = value; + return this; + } + + /** + * Sets the player version. + */ + public @NonNull Builder setPlayerVersion(@NonNull String value) { + mPlayerVersion = value; + return this; + } + + /** + * Adds the experiment ID. + */ + public @NonNull Builder addExperimentId(long value) { + mExperimentIds.add(value); + return this; + } + + /** + * Sets the video frames played. + */ + public @NonNull Builder setVideoFramesPlayed(int value) { + mVideoFramesPlayed = value; + return this; + } + + /** + * Sets the video frames dropped. + */ + public @NonNull Builder setVideoFramesDropped(int value) { + mVideoFramesDropped = value; + return this; + } + + /** + * Sets the audio underrun count. + */ + public @NonNull Builder setAudioUnderrunCount(int value) { + mAudioUnderrunCount = value; + return this; + } + + /** + * Sets the number of network bytes read. + */ + public @NonNull Builder setNetworkBytesRead(long value) { + mNetworkBytesRead = value; + return this; + } + + /** + * Sets the number of local bytes read. + */ + public @NonNull Builder setLocalBytesRead(long value) { + mLocalBytesRead = value; + return this; + } + + /** + * Sets the network transfer duration in milliseconds. + */ + public @NonNull Builder setNetworkTransferDurationMillis(long value) { + mNetworkTransferDurationMillis = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull PlaybackMetrics build() { + + PlaybackMetrics o = new PlaybackMetrics( + mMediaDurationMillis, + mStreamSource, + mStreamType, + mPlaybackType, + mDrmType, + mContentType, + mPlayerName, + mPlayerVersion, + idsToLongArray(), + mVideoFramesPlayed, + mVideoFramesDropped, + mAudioUnderrunCount, + mNetworkBytesRead, + mLocalBytesRead, + mNetworkTransferDurationMillis); + return o; + } + + private long[] idsToLongArray() { + long[] ids = new long[mExperimentIds.size()]; + for (int i = 0; i < mExperimentIds.size(); i++) { + ids[i] = mExperimentIds.get(i); + } + return ids; + } + } } diff --git a/media/java/android/media/metrics/PlaybackMetricsManager.java b/media/java/android/media/metrics/PlaybackMetricsManager.java index 95f64ea9f11e..f48ffe7f3b22 100644 --- a/media/java/android/media/metrics/PlaybackMetricsManager.java +++ b/media/java/android/media/metrics/PlaybackMetricsManager.java @@ -61,6 +61,30 @@ public class PlaybackMetricsManager { } /** + * Reports playback state event. + * @hide + */ + public void reportPlaybackStateEvent(@NonNull String sessionId, PlaybackStateEvent event) { + try { + mService.reportPlaybackStateEvent(sessionId, event, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Reports track change event. + * @hide + */ + public void reportTrackChangeEvent(@NonNull String sessionId, TrackChangeEvent event) { + try { + mService.reportTrackChangeEvent(sessionId, event, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Creates a playback session. */ public PlaybackSession createSession() { diff --git a/media/java/android/media/metrics/PlaybackSession.java b/media/java/android/media/metrics/PlaybackSession.java index 3e2f4e1f4c9a..0a77516a0b8c 100644 --- a/media/java/android/media/metrics/PlaybackSession.java +++ b/media/java/android/media/metrics/PlaybackSession.java @@ -64,6 +64,20 @@ public final class PlaybackSession implements AutoCloseable { mManager.reportNetworkEvent(mId, event); } + /** + * Reports playback state event. + */ + public void reportPlaybackStateEvent(PlaybackStateEvent event) { + mManager.reportPlaybackStateEvent(mId, event); + } + + /** + * Reports track change event. + */ + public void reportTrackChangeEvent(TrackChangeEvent event) { + mManager.reportTrackChangeEvent(mId, event); + } + public @NonNull String getId() { return mId; } diff --git a/media/java/android/media/metrics/PlaybackStateEvent.aidl b/media/java/android/media/metrics/PlaybackStateEvent.aidl new file mode 100644 index 000000000000..8b8d05bd4093 --- /dev/null +++ b/media/java/android/media/metrics/PlaybackStateEvent.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.metrics; + +parcelable PlaybackStateEvent; diff --git a/media/java/android/media/metrics/PlaybackStateEvent.java b/media/java/android/media/metrics/PlaybackStateEvent.java new file mode 100644 index 000000000000..6ce5bf0f0f33 --- /dev/null +++ b/media/java/android/media/metrics/PlaybackStateEvent.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.metrics; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.util.Objects; + +/** + * Playback state event. + * @hide + */ +public final class PlaybackStateEvent implements Parcelable { + // TODO: more states + /** Playback has not started (initial state) */ + public static final int STATE_NOT_STARTED = 0; + /** Playback is buffering in the background for initial playback start */ + public static final int STATE_JOINING_BACKGROUND = 1; + /** Playback is buffering in the foreground for initial playback start */ + public static final int STATE_JOINING_FOREGROUND = 2; + /** Playback is actively playing */ + public static final int STATE_PLAYING = 3; + /** Playback is paused but ready to play */ + public static final int STATE_PAUSED = 4; + + private int mState; + private long mTimeSincePlaybackCreatedMillis; + + // These track ExoPlayer states. See the ExoPlayer documentation for the state transitions. + @IntDef(prefix = "STATE_", value = { + STATE_NOT_STARTED, + STATE_JOINING_BACKGROUND, + STATE_JOINING_FOREGROUND, + STATE_PLAYING, + STATE_PAUSED + }) + @Retention(java.lang.annotation.RetentionPolicy.SOURCE) + public @interface State {} + + /** + * Converts playback state to string. + */ + public static String stateToString(@State int value) { + switch (value) { + case STATE_NOT_STARTED: + return "STATE_NOT_STARTED"; + case STATE_JOINING_BACKGROUND: + return "STATE_JOINING_BACKGROUND"; + case STATE_JOINING_FOREGROUND: + return "STATE_JOINING_FOREGROUND"; + case STATE_PLAYING: + return "STATE_PLAYING"; + case STATE_PAUSED: + return "STATE_PAUSED"; + default: + return Integer.toHexString(value); + } + } + + /** + * Creates a new PlaybackStateEvent. + * + * @hide + */ + public PlaybackStateEvent( + int state, + long timeSincePlaybackCreatedMillis) { + this.mState = state; + this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis; + } + + /** + * Gets playback state. + * @return + */ + public int getState() { + return mState; + } + + /** + * Gets time since the corresponding playback is created in millisecond. + */ + public long getTimeSincePlaybackCreatedMillis() { + return mTimeSincePlaybackCreatedMillis; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PlaybackStateEvent that = (PlaybackStateEvent) o; + return mState == that.mState + && mTimeSincePlaybackCreatedMillis == that.mTimeSincePlaybackCreatedMillis; + } + + @Override + public int hashCode() { + return Objects.hash(mState, mTimeSincePlaybackCreatedMillis); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mState); + dest.writeLong(mTimeSincePlaybackCreatedMillis); + } + + @Override + public int describeContents() { + return 0; + } + + /** @hide */ + /* package-private */ PlaybackStateEvent(@NonNull Parcel in) { + int state = in.readInt(); + long timeSincePlaybackCreatedMillis = in.readLong(); + + this.mState = state; + this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis; + } + + public static final @NonNull Parcelable.Creator<PlaybackStateEvent> CREATOR = + new Parcelable.Creator<PlaybackStateEvent>() { + @Override + public PlaybackStateEvent[] newArray(int size) { + return new PlaybackStateEvent[size]; + } + + @Override + public PlaybackStateEvent createFromParcel(@NonNull Parcel in) { + return new PlaybackStateEvent(in); + } + }; + +} diff --git a/media/java/android/media/metrics/TrackChangeEvent.aidl b/media/java/android/media/metrics/TrackChangeEvent.aidl new file mode 100644 index 000000000000..8fbe4a9eb66c --- /dev/null +++ b/media/java/android/media/metrics/TrackChangeEvent.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.metrics; + +parcelable TrackChangeEvent; diff --git a/media/java/android/media/metrics/TrackChangeEvent.java b/media/java/android/media/metrics/TrackChangeEvent.java new file mode 100644 index 000000000000..fff0e36c062a --- /dev/null +++ b/media/java/android/media/metrics/TrackChangeEvent.java @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.metrics; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Playback track change event. + * @hide + */ +public final class TrackChangeEvent implements Parcelable { + public static final int TRACK_STATE_OFF = 0; + public static final int TRACK_STATE_ON = 1; + + public static final int TRACK_CHANGE_REASON_UNKNOWN = 0; + public static final int TRACK_CHANGE_REASON_OTHER = 1; + public static final int TRACK_CHANGE_REASON_INITIAL = 2; + public static final int TRACK_CHANGE_REASON_MANUAL = 3; + public static final int TRACK_CHANGE_REASON_ADAPTIVE = 4; + + public static final int TRACK_TYPE_AUDIO = 0; + public static final int TRACK_TYPE_VIDEO = 1; + public static final int TRACK_TYPE_TEXT = 2; + + private final int mState; + private final int mReason; + private final @Nullable String mContainerMimeType; + private final @Nullable String mSampleMimeType; + private final @Nullable String mCodecName; + private final int mBitrate; + private final long mTimeSincePlaybackCreatedMillis; + private final int mType; + private final @Nullable String mLanguage; + private final @Nullable String mLanguageRegion; + private final int mChannelCount; + private final int mSampleRate; + private final int mWidth; + private final int mHeight; + + + + /** @hide */ + @IntDef(prefix = "TRACK_STATE_", value = { + TRACK_STATE_OFF, + TRACK_STATE_ON + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TrackState {} + + /** @hide */ + @IntDef(prefix = "TRACK_CHANGE_REASON_", value = { + TRACK_CHANGE_REASON_UNKNOWN, + TRACK_CHANGE_REASON_OTHER, + TRACK_CHANGE_REASON_INITIAL, + TRACK_CHANGE_REASON_MANUAL, + TRACK_CHANGE_REASON_ADAPTIVE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TrackChangeReason {} + + /** @hide */ + @IntDef(prefix = "TRACK_TYPE_", value = { + TRACK_TYPE_AUDIO, + TRACK_TYPE_VIDEO, + TRACK_TYPE_TEXT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TrackType {} + + public TrackChangeEvent( + int state, + int reason, + @Nullable String containerMimeType, + @Nullable String sampleMimeType, + @Nullable String codecName, + int bitrate, + long timeSincePlaybackCreatedMillis, + int type, + @Nullable String language, + @Nullable String languageRegion, + int channelCount, + int sampleRate, + int width, + int height) { + this.mState = state; + this.mReason = reason; + this.mContainerMimeType = containerMimeType; + this.mSampleMimeType = sampleMimeType; + this.mCodecName = codecName; + this.mBitrate = bitrate; + this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis; + this.mType = type; + this.mLanguage = language; + this.mLanguageRegion = languageRegion; + this.mChannelCount = channelCount; + this.mSampleRate = sampleRate; + this.mWidth = width; + this.mHeight = height; + } + + @TrackState + public int getTrackState() { + return mState; + } + + @TrackChangeReason + public int getTrackChangeReason() { + return mReason; + } + + public @Nullable String getContainerMimeType() { + return mContainerMimeType; + } + + public @Nullable String getSampleMimeType() { + return mSampleMimeType; + } + + public @Nullable String getCodecName() { + return mCodecName; + } + + public int getBitrate() { + return mBitrate; + } + + public long getTimeSincePlaybackCreatedMillis() { + return mTimeSincePlaybackCreatedMillis; + } + + @TrackType + public int getTrackType() { + return mType; + } + + public @Nullable String getLanguage() { + return mLanguage; + } + + public @Nullable String getLanguageRegion() { + return mLanguageRegion; + } + + public int getChannelCount() { + return mChannelCount; + } + + public int getSampleRate() { + return mSampleRate; + } + + public int getWidth() { + return mWidth; + } + + public int getHeight() { + return mHeight; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + int flg = 0; + if (mContainerMimeType != null) flg |= 0x4; + if (mSampleMimeType != null) flg |= 0x8; + if (mCodecName != null) flg |= 0x10; + if (mLanguage != null) flg |= 0x100; + if (mLanguageRegion != null) flg |= 0x200; + dest.writeInt(flg); + dest.writeInt(mState); + dest.writeInt(mReason); + if (mContainerMimeType != null) dest.writeString(mContainerMimeType); + if (mSampleMimeType != null) dest.writeString(mSampleMimeType); + if (mCodecName != null) dest.writeString(mCodecName); + dest.writeInt(mBitrate); + dest.writeLong(mTimeSincePlaybackCreatedMillis); + dest.writeInt(mType); + if (mLanguage != null) dest.writeString(mLanguage); + if (mLanguageRegion != null) dest.writeString(mLanguageRegion); + dest.writeInt(mChannelCount); + dest.writeInt(mSampleRate); + dest.writeInt(mWidth); + dest.writeInt(mHeight); + } + + @Override + public int describeContents() { + return 0; + } + + /** @hide */ + /* package-private */ TrackChangeEvent(@NonNull Parcel in) { + int flg = in.readInt(); + int state = in.readInt(); + int reason = in.readInt(); + String containerMimeType = (flg & 0x4) == 0 ? null : in.readString(); + String sampleMimeType = (flg & 0x8) == 0 ? null : in.readString(); + String codecName = (flg & 0x10) == 0 ? null : in.readString(); + int bitrate = in.readInt(); + long timeSincePlaybackCreatedMillis = in.readLong(); + int type = in.readInt(); + String language = (flg & 0x100) == 0 ? null : in.readString(); + String languageRegion = (flg & 0x200) == 0 ? null : in.readString(); + int channelCount = in.readInt(); + int sampleRate = in.readInt(); + int width = in.readInt(); + int height = in.readInt(); + + this.mState = state; + this.mReason = reason; + this.mContainerMimeType = containerMimeType; + this.mSampleMimeType = sampleMimeType; + this.mCodecName = codecName; + this.mBitrate = bitrate; + this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis; + this.mType = type; + this.mLanguage = language; + this.mLanguageRegion = languageRegion; + this.mChannelCount = channelCount; + this.mSampleRate = sampleRate; + this.mWidth = width; + this.mHeight = height; + } + + public static final @NonNull Parcelable.Creator<TrackChangeEvent> CREATOR = + new Parcelable.Creator<TrackChangeEvent>() { + @Override + public TrackChangeEvent[] newArray(int size) { + return new TrackChangeEvent[size]; + } + + @Override + public TrackChangeEvent createFromParcel(@NonNull Parcel in) { + return new TrackChangeEvent(in); + } + }; + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/media/java/android/media/metrics/TrackChangeEvent.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + @Override + public String toString() { + return "TrackChangeEvent { " + + "state = " + mState + ", " + + "reason = " + mReason + ", " + + "containerMimeType = " + mContainerMimeType + ", " + + "sampleMimeType = " + mSampleMimeType + ", " + + "codecName = " + mCodecName + ", " + + "bitrate = " + mBitrate + ", " + + "timeSincePlaybackCreatedMillis = " + mTimeSincePlaybackCreatedMillis + ", " + + "type = " + mType + ", " + + "language = " + mLanguage + ", " + + "languageRegion = " + mLanguageRegion + ", " + + "channelCount = " + mChannelCount + ", " + + "sampleRate = " + mSampleRate + ", " + + "width = " + mWidth + ", " + + "height = " + mHeight + + " }"; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TrackChangeEvent that = (TrackChangeEvent) o; + return mState == that.mState + && mReason == that.mReason + && Objects.equals(mContainerMimeType, that.mContainerMimeType) + && Objects.equals(mSampleMimeType, that.mSampleMimeType) + && Objects.equals(mCodecName, that.mCodecName) + && mBitrate == that.mBitrate + && mTimeSincePlaybackCreatedMillis == that.mTimeSincePlaybackCreatedMillis + && mType == that.mType + && Objects.equals(mLanguage, that.mLanguage) + && Objects.equals(mLanguageRegion, that.mLanguageRegion) + && mChannelCount == that.mChannelCount + && mSampleRate == that.mSampleRate + && mWidth == that.mWidth + && mHeight == that.mHeight; + } + + @Override + public int hashCode() { + return Objects.hash(mState, mReason, mContainerMimeType, mSampleMimeType, mCodecName, + mBitrate, mTimeSincePlaybackCreatedMillis, mType, mLanguage, mLanguageRegion, + mChannelCount, mSampleRate, mWidth, mHeight); + } + + /** + * A builder for {@link TrackChangeEvent} + */ + public static final class Builder { + // TODO: check track type for the setters. + private int mState; + private int mReason; + private @Nullable String mContainerMimeType; + private @Nullable String mSampleMimeType; + private @Nullable String mCodecName; + private int mBitrate; + private long mTimeSincePlaybackCreatedMillis; + private int mType; + private @Nullable String mLanguage; + private @Nullable String mLanguageRegion; + private int mChannelCount; + private int mSampleRate; + private int mWidth; + private int mHeight; + + private long mBuilderFieldsSet = 0L; + + /** + * Creates a new Builder. + * + * @hide + */ + public Builder(int type) { + mType = type; + } + + public @NonNull Builder setTrackState(@TrackState int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mState = value; + return this; + } + + public @NonNull Builder setTrackChangeReason(@TrackChangeReason int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mReason = value; + return this; + } + + public @NonNull Builder setContainerMimeType(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; + mContainerMimeType = value; + return this; + } + + public @NonNull Builder setSampleMimeType(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x8; + mSampleMimeType = value; + return this; + } + + public @NonNull Builder setCodecName(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x10; + mCodecName = value; + return this; + } + + public @NonNull Builder setBitrate(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x20; + mBitrate = value; + return this; + } + + public @NonNull Builder setTimeSincePlaybackCreatedMillis(long value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x40; + mTimeSincePlaybackCreatedMillis = value; + return this; + } + + public @NonNull Builder setTrackType(@TrackType int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x80; + mType = value; + return this; + } + + public @NonNull Builder setLanguage(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x100; + mLanguage = value; + return this; + } + + public @NonNull Builder setLanguageRegion(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x200; + mLanguageRegion = value; + return this; + } + + public @NonNull Builder setChannelCount(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x400; + mChannelCount = value; + return this; + } + + public @NonNull Builder setSampleRate(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x800; + mSampleRate = value; + return this; + } + + public @NonNull Builder setWidth(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1000; + mWidth = value; + return this; + } + + public @NonNull Builder setHeight(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2000; + mHeight = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull TrackChangeEvent build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x4000; // Mark builder used + + TrackChangeEvent o = new TrackChangeEvent( + mState, + mReason, + mContainerMimeType, + mSampleMimeType, + mCodecName, + mBitrate, + mTimeSincePlaybackCreatedMillis, + mType, + mLanguage, + mLanguageRegion, + mChannelCount, + mSampleRate, + mWidth, + mHeight); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x4000) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } +} diff --git a/media/java/android/media/tv/TvInputHardwareInfo.java b/media/java/android/media/tv/TvInputHardwareInfo.java index b12f7c551288..1249e0d3cb0f 100644 --- a/media/java/android/media/tv/TvInputHardwareInfo.java +++ b/media/java/android/media/tv/TvInputHardwareInfo.java @@ -188,6 +188,17 @@ public final class TvInputHardwareInfo implements Parcelable { mCableConnectionStatus = source.readInt(); } + /** @hide */ + public Builder toBuilder() { + return new Builder() + .deviceId(mDeviceId) + .type(mType) + .audioType(mAudioType) + .audioAddress(mAudioAddress) + .hdmiPortId(mHdmiPortId) + .cableConnectionStatus(mCableConnectionStatus); + } + public static final class Builder { private Integer mDeviceId = null; private Integer mType = null; diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 46b29f5bc90a..7192c0737b77 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -988,9 +988,11 @@ public class Tuner implements AutoCloseable { } /** - * Gets the initialized frontend information. + * Gets the currently initialized and activated frontend information. To get all the available + * frontend info on the device, use {@link getAvailableFrontendInfos()}. * - * @return The frontend information. {@code null} if the operation failed. + * @return The active frontend information. {@code null} if the operation failed. + * @throws IllegalStateException if there is no active frontend currently. */ @Nullable public FrontendInfo getFrontendInfo() { @@ -1007,13 +1009,20 @@ public class Tuner implements AutoCloseable { } /** - * Get a list all the existed frontend information. + * Gets a list of all the available frontend information on the device. To get the information + * of the currently active frontend, use {@link getFrontendInfo()}. The active frontend + * information is also included in the list of the available frontend information. * - * @return The list of all the frontend information. {@code null} if the operation failed. + * @return The list of all the available frontend information. {@code null} if the operation + * failed. */ @Nullable - public List<FrontendInfo> getFrontendInfoList() { - return Arrays.asList(getFrontendInfoListInternal()); + public List<FrontendInfo> getAvailableFrontendInfos() { + FrontendInfo[] feInfoList = getFrontendInfoListInternal(); + if (feInfoList == null) { + return null; + } + return Arrays.asList(feInfoList); } /** @hide */ diff --git a/media/java/android/media/tv/tunerresourcemanager/Android.bp b/media/java/android/media/tv/tunerresourcemanager/Android.bp index 02390bb7c31b..c38d9194df44 100644 --- a/media/java/android/media/tv/tunerresourcemanager/Android.bp +++ b/media/java/android/media/tv/tunerresourcemanager/Android.bp @@ -1,16 +1,7 @@ filegroup { name: "framework-media-tv-tunerresourcemanager-sources-aidl", srcs: [ - "aidl/android/media/tv/tunerresourcemanager/CasSessionRequest.aidl", - "aidl/android/media/tv/tunerresourcemanager/IResourcesReclaimListener.aidl", - "aidl/android/media/tv/tunerresourcemanager/ResourceClientProfile.aidl", - "aidl/android/media/tv/tunerresourcemanager/TunerCiCamRequest.aidl", - "aidl/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl", - "aidl/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl", - "aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl", - "aidl/android/media/tv/tunerresourcemanager/TunerFrontendRequest.aidl", - "aidl/android/media/tv/tunerresourcemanager/TunerLnbRequest.aidl", - "aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl", + "aidl/android/media/tv/tunerresourcemanager/*.aidl", ], path: "aidl", } @@ -21,7 +12,7 @@ aidl_interface { local_include_dir: "aidl", backend: { java: { - sdk_version: "current", + enabled: true, }, cpp: { enabled: true, @@ -33,4 +24,5 @@ aidl_interface { srcs: [ ":framework-media-tv-tunerresourcemanager-sources-aidl", ], + imports: ["tv_tuner_frontend_info_aidl_interface"], } diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java index 6f7adbc65318..e399fbdfabcf 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java +++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java @@ -24,6 +24,7 @@ import android.annotation.RequiresFeature; import android.annotation.SystemService; import android.content.Context; import android.content.pm.PackageManager; +import android.media.tv.tuner.TunerFrontendInfo; import android.os.Binder; import android.os.RemoteException; import android.util.Log; diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl index a1f6687a1b81..483d9720b430 100644 --- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl @@ -16,13 +16,13 @@ package android.media.tv.tunerresourcemanager; +import android.media.tv.tuner.TunerFrontendInfo; import android.media.tv.tunerresourcemanager.CasSessionRequest; import android.media.tv.tunerresourcemanager.IResourcesReclaimListener; import android.media.tv.tunerresourcemanager.ResourceClientProfile; import android.media.tv.tunerresourcemanager.TunerCiCamRequest; import android.media.tv.tunerresourcemanager.TunerDemuxRequest; import android.media.tv.tunerresourcemanager.TunerDescramblerRequest; -import android.media.tv.tunerresourcemanager.TunerFrontendInfo; import android.media.tv.tunerresourcemanager.TunerFrontendRequest; import android.media.tv.tunerresourcemanager.TunerLnbRequest; diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index 798bf6e2f8ee..4f27b197273c 100755 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java @@ -27,10 +27,14 @@ import android.content.SharedPreferences; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; +import android.media.ApplicationMediaCapabilities; import android.media.ExifInterface; +import android.media.MediaFormat; import android.media.ThumbnailUtils; import android.net.Uri; import android.os.BatteryManager; +import android.os.Bundle; +import android.os.RemoteException; import android.os.SystemProperties; import android.os.storage.StorageVolume; import android.provider.MediaStore; @@ -52,6 +56,7 @@ import com.google.android.collect.Sets; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; @@ -754,6 +759,32 @@ public class MtpDatabase implements AutoCloseable { return MtpConstants.RESPONSE_OK; } + @VisibleForNative + private int openFilePath(String path, boolean transcode) { + Uri uri = MediaStore.scanFile(mContext.getContentResolver(), new File(path)); + if (uri == null) { + Log.i(TAG, "Failed to obtain URI for openFile with transcode support: " + path); + return -1; + } + + try { + Log.i(TAG, "openFile with transcode support: " + path); + // TODO(b/158466651): Pass the |transcode| variable as flag to openFile + Bundle bundle = null; + if (!transcode) { + bundle = new Bundle(); + bundle.putParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES, + new ApplicationMediaCapabilities.Builder().addSupportedVideoMimeType( + MediaFormat.MIMETYPE_VIDEO_HEVC).build()); + } + return mMediaProvider.openTypedAssetFileDescriptor(uri, "*/*", bundle) + .getParcelFileDescriptor().detachFd(); + } catch (RemoteException | FileNotFoundException e) { + Log.w(TAG, "Failed to openFile with transcode support: " + path, e); + return -1; + } + } + private int getObjectFormat(int handle) { MtpStorageManager.MtpObject obj = mManager.getObject(handle); if (obj == null) { diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index 17189fd08688..4efdcaccf7a1 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -63,6 +63,7 @@ static jmethodID method_setDeviceProperty; static jmethodID method_getObjectPropertyList; static jmethodID method_getObjectInfo; static jmethodID method_getObjectFilePath; +static jmethodID method_openFilePath; static jmethodID method_getThumbnailInfo; static jmethodID method_getThumbnailData; static jmethodID method_beginDeleteObject; @@ -160,6 +161,7 @@ public: MtpStringBuffer& outFilePath, int64_t& outFileLength, MtpObjectFormat& outFormat); + virtual int openFilePath(const char* path, bool transcode); virtual MtpResponseCode beginDeleteObject(MtpObjectHandle handle); virtual void endDeleteObject(MtpObjectHandle handle, bool succeeded); @@ -969,6 +971,17 @@ MtpResponseCode MtpDatabase::getObjectFilePath(MtpObjectHandle handle, return result; } +int MtpDatabase::openFilePath(const char* path, bool transcode) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jstring pathStr = env->NewStringUTF(path); + jint result = env->CallIntMethod(mDatabase, method_openFilePath, pathStr, transcode); + + if (result < 0) { + checkAndClearExceptionFromCallback(env, __FUNCTION__); + } + return result; +} + MtpResponseCode MtpDatabase::beginDeleteObject(MtpObjectHandle handle) { JNIEnv* env = AndroidRuntime::getJNIEnv(); MtpResponseCode result = env->CallIntMethod(mDatabase, method_beginDeleteObject, (jint)handle); @@ -1333,6 +1346,7 @@ int register_android_mtp_MtpDatabase(JNIEnv *env) GET_METHOD_ID(getObjectPropertyList, clazz, "(IIIII)Landroid/mtp/MtpPropertyList;"); GET_METHOD_ID(getObjectInfo, clazz, "(I[I[C[J)Z"); GET_METHOD_ID(getObjectFilePath, clazz, "(I[C[J)I"); + GET_METHOD_ID(openFilePath, clazz, "(Ljava/lang/String;Z)I"); GET_METHOD_ID(getThumbnailInfo, clazz, "(I[J)Z"); GET_METHOD_ID(getThumbnailData, clazz, "(I)[B"); GET_METHOD_ID(beginDeleteObject, clazz, "(I)I"); diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp index a26753dd65a6..f5e35248fcfd 100644 --- a/media/jni/tuner/TunerClient.cpp +++ b/media/jni/tuner/TunerClient.cpp @@ -25,8 +25,6 @@ using ::android::hardware::tv::tuner::V1_0::FrontendId; using ::android::hardware::tv::tuner::V1_0::FrontendType; -using ::aidl::android::media::tv::tunerresourcemanager::TunerFrontendInfo; - namespace android { sp<ITuner> TunerClient::mTuner; @@ -104,7 +102,7 @@ sp<FrontendClient> TunerClient::openFrontend(int frontendHandle) { int id; // TODO: handle error code tunerFrontend->getFrontendId(&id); - TunerServiceFrontendInfo aidlFrontendInfo; + TunerFrontendInfo aidlFrontendInfo; // TODO: handle error code mTunerService->getFrontendInfo(id, &aidlFrontendInfo); return new FrontendClient(tunerFrontend, frontendHandle, aidlFrontendInfo.type); @@ -130,7 +128,7 @@ sp<FrontendClient> TunerClient::openFrontend(int frontendHandle) { shared_ptr<FrontendInfo> TunerClient::getFrontendInfo(int id) { if (mTunerService != NULL) { - TunerServiceFrontendInfo aidlFrontendInfo; + TunerFrontendInfo aidlFrontendInfo; // TODO: handle error code mTunerService->getFrontendInfo(id, &aidlFrontendInfo); return make_shared<FrontendInfo>(FrontendInfoAidlToHidl(aidlFrontendInfo)); @@ -303,7 +301,7 @@ void TunerClient::updateFrontendResources() { } TunerFrontendInfo tunerFrontendInfo{ .handle = getResourceHandleFromId((int)ids[i], FRONTEND), - .frontendType = static_cast<int>(frontendInfo->type), + .type = static_cast<int>(frontendInfo->type), .exclusiveGroupId = static_cast<int>(frontendInfo->exclusiveGroupId), }; infos.push_back(tunerFrontendInfo); @@ -452,7 +450,7 @@ vector<int> TunerClient::getLnbHandles() { return lnbHandles; } -FrontendInfo TunerClient::FrontendInfoAidlToHidl(TunerServiceFrontendInfo aidlFrontendInfo) { +FrontendInfo TunerClient::FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo) { FrontendInfo hidlFrontendInfo { .type = static_cast<FrontendType>(aidlFrontendInfo.type), .minFrequency = static_cast<uint32_t>(aidlFrontendInfo.minFrequency), diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h index a3d2d02c7ef6..8a1181a38fe2 100644 --- a/media/jni/tuner/TunerClient.h +++ b/media/jni/tuner/TunerClient.h @@ -18,8 +18,8 @@ #define _ANDROID_MEDIA_TV_TUNER_CLIENT_H_ #include <aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.h> -#include <aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.h> #include <aidl/android/media/tv/tuner/ITunerService.h> +#include <aidl/android/media/tv/tuner/TunerFrontendInfo.h> #include <android/hardware/tv/tuner/1.1/ITuner.h> #include <android/hardware/tv/tuner/1.1/types.h> @@ -29,7 +29,7 @@ #include "LnbClient.h" using ::aidl::android::media::tv::tuner::ITunerService; -using ::aidl::android::media::tv::tuner::TunerServiceFrontendInfo; +using ::aidl::android::media::tv::tuner::TunerFrontendInfo; using ::aidl::android::media::tv::tunerresourcemanager::ITunerResourceManager; using ::android::hardware::tv::tuner::V1_0::DemuxCapabilities; @@ -141,7 +141,7 @@ private: sp<ILnb> openHidlLnbByName(string name, LnbId& lnbId); sp<IDescrambler> openHidlDescrambler(); vector<int> getLnbHandles(); - FrontendInfo FrontendInfoAidlToHidl(TunerServiceFrontendInfo aidlFrontendInfo); + FrontendInfo FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo); void updateTunerResources(); void updateFrontendResources(); void updateLnbResources(); diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp index b9aefdd18191..3751564ca465 100644 --- a/native/graphics/jni/Android.bp +++ b/native/graphics/jni/Android.bp @@ -89,6 +89,13 @@ cc_fuzz { asan_options: [ "detect_odr_violation=1", ], + hwasan_options: [ + // Image decoders may attempt to allocate a large amount of memory + // (especially if the encoded image is large). This doesn't + // necessarily mean there is a bug. Set allocator_may_return_null=1 + // for hwasan so the fuzzer can continue running. + "allocator_may_return_null = 1", + ], }, corpus: ["corpus/*"], host_supported: true, diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp index eab5f4143968..385e455e3e1f 100644 --- a/native/graphics/jni/imagedecoder.cpp +++ b/native/graphics/jni/imagedecoder.cpp @@ -353,7 +353,7 @@ int AImageDecoder_computeSampledSize(const AImageDecoder* decoder, int sampleSiz return ANDROID_IMAGE_DECODER_BAD_PARAMETER; } - SkISize size = toDecoder(decoder)->mCodec->getSampledDimensions(sampleSize); + SkISize size = toDecoder(decoder)->getSampledDimensions(sampleSize); *width = size.width(); *height = size.height(); return ANDROID_IMAGE_DECODER_SUCCESS; diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml index d366a0be94a5..cdf4851a3e3d 100644 --- a/packages/CompanionDeviceManager/res/values-af/strings.xml +++ b/packages/CompanionDeviceManager/res/values-af/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Metgeseltoestel-bestuurder"</string> <string name="chooser_title" msgid="2262294130493605839">"Kies \'n <xliff:g id="PROFILE_NAME">%1$s</xliff:g> om deur <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> bestuur te word"</string> <string name="profile_name_generic" msgid="6851028682723034988">"toestel"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"horlosie"</string> <string name="confirmation_title" msgid="4751119145078041732">"Stel <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> om jou <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te bestuur – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Jy het <xliff:g id="APP_NAME">%1$s</xliff:g> nodig om jou <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te bestuur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Ja"</string> <string name="consent_no" msgid="1335543792857823917">"Nee, dankie"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml index 68e816adf726..a03ea0dfcd4c 100644 --- a/packages/CompanionDeviceManager/res/values-am/strings.xml +++ b/packages/CompanionDeviceManager/res/values-am/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"አጃቢ የመሣሪያ አስተዳዳሪ"</string> <string name="chooser_title" msgid="2262294130493605839">"በ<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> የሚተዳደር <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ይምረጡ"</string> <string name="profile_name_generic" msgid="6851028682723034988">"መሣሪያ"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"ሰዓት"</string> <string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> የእርስዎን <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> እንዲያስተዳድር ያቀናብሩት"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> የእርስዎን <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ለማስተዳደር ያስፈልጋል። <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"አዎ"</string> <string name="consent_no" msgid="1335543792857823917">"አይ፣ አመሰግናለሁ"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml index 184837f692e8..970c46bd6133 100644 --- a/packages/CompanionDeviceManager/res/values-ar/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"تطبيق \"مدير الجهاز المصاحب\""</string> <string name="chooser_title" msgid="2262294130493605839">"اختَر <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ليديره تطبيق <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"جهاز"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"ساعة"</string> <string name="confirmation_title" msgid="4751119145078041732">"اضبط <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> لإدارة <xliff:g id="PROFILE_NAME">%2$s</xliff:g> على <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"يجب توفّر تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> لإدارة <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"نعم"</string> <string name="consent_no" msgid="1335543792857823917">"لا، شكرًا"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml index 116d63b3d8d5..477844c2a477 100644 --- a/packages/CompanionDeviceManager/res/values-as/strings.xml +++ b/packages/CompanionDeviceManager/res/values-as/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"কম্পেনিয়ন ডিভাইচ মেনেজাৰ"</string> <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>এ পৰিচালনা কৰিব লগা এটা <xliff:g id="PROFILE_NAME">%1$s</xliff:g> বাছনি কৰক"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইচ"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"ঘড়ী"</string> <string name="confirmation_title" msgid="4751119145078041732">"আপোনাৰ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> পৰিচালনা কৰিবলৈ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ছেট কৰক - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"আপোনাৰ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> পৰিচালনা কৰিবলৈ <xliff:g id="APP_NAME">%1$s</xliff:g>ৰ আৱশ্যক। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"হয়"</string> <string name="consent_no" msgid="1335543792857823917">"নালাগে, ধন্যবাদ"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml index bc719a340074..f10c639a0368 100644 --- a/packages/CompanionDeviceManager/res/values-az/strings.xml +++ b/packages/CompanionDeviceManager/res/values-az/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Kompanyon Cihaz Meneceri"</string> <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> tərəfindən idarə ediləcək <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string> <string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"izləyin"</string> <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> profilinizin <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tərəfindən idarə olunmasını ayarlayın - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> profilinizi idarə etmək üçün <xliff:g id="APP_NAME">%1$s</xliff:g> tələb olunur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Bəli"</string> <string name="consent_no" msgid="1335543792857823917">"Xeyr, çox sağolun"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml index a0e52989c964..e8542f364027 100644 --- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Menadžer pridruženog uređaja"</string> <string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"sat"</string> <string name="confirmation_title" msgid="4751119145078041732">"Podesite aplikaciju <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da upravlja profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je neophodna za upravljanje profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Da"</string> <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml index 96a513dff683..13be6f245ac0 100644 --- a/packages/CompanionDeviceManager/res/values-be/strings.xml +++ b/packages/CompanionDeviceManager/res/values-be/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Менеджар спадарожнай прылады"</string> <string name="chooser_title" msgid="2262294130493605839">"Выберыце прыладу (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), якой будзе кіраваць праграма <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"прылада"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"гадзіннік"</string> <string name="confirmation_title" msgid="4751119145078041732">"Дазвольце праграме <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> кіраваць прыладай \"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>\" – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Для кіравання прыладай \"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>\" патрабуецца праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Так"</string> <string name="consent_no" msgid="1335543792857823917">"Не, дзякуй"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml index 3775dbe87b38..3bda5e6104c8 100644 --- a/packages/CompanionDeviceManager/res/values-bg/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"Изберете устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), което да се управлява от <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"часовник"</string> <string name="confirmation_title" msgid="4751119145078041732">"Задайте <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да управлява устройството ви (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"За управление на <xliff:g id="PROFILE_NAME">%2$s</xliff:g> се изисква <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Да"</string> <string name="consent_no" msgid="1335543792857823917">"Не, благодаря"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml index 1e8fd0be40c3..d3bc5152af9a 100644 --- a/packages/CompanionDeviceManager/res/values-bn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> বেছে নিন যেটি <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ম্যানেজ করবে"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইস"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"দেখুন"</string> <string name="confirmation_title" msgid="4751119145078041732">"আপনার <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> ম্যানেজ করার জন্য <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> সেট করুন"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g>-কে আপনার <xliff:g id="PROFILE_NAME">%2$s</xliff:g>.ম্যানেজ করতে দিতে হবে। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"হ্যাঁ"</string> <string name="consent_no" msgid="1335543792857823917">"না থাক"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml index 133aea35753e..905b3061ffc6 100644 --- a/packages/CompanionDeviceManager/res/values-bs/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Prateći upravitelj uređaja"</string> <string name="chooser_title" msgid="2262294130493605839">"Odaberite uređaj <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"sat"</string> <string name="confirmation_title" msgid="4751119145078041732">"Postavite aplikaciju <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da upravlja vašim uređajem <xliff:g id="PROFILE_NAME">%2$s</xliff:g> — <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Potrebna je aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> za upravljanje uređajem <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Da"</string> <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml index cf26b08c949b..86dc6940abe2 100644 --- a/packages/CompanionDeviceManager/res/values-ca/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Gestor de dispositius complementaris"</string> <string name="chooser_title" msgid="2262294130493605839">"Tria un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> perquè el gestioni <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositiu"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"rellotge"</string> <string name="confirmation_title" msgid="4751119145078041732">"Defineix que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gestioni el teu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (<strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>)"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"L\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g> és necessària per gestionar el teu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Sí"</string> <string name="consent_no" msgid="1335543792857823917">"No, gràcies"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml index 30a331a9e699..5a31f9bba4b6 100644 --- a/packages/CompanionDeviceManager/res/values-da/strings.xml +++ b/packages/CompanionDeviceManager/res/values-da/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Medfølgende enhedshåndtering"</string> <string name="chooser_title" msgid="2262294130493605839">"Vælg den enhed (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), som skal administreres af <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"enhed"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"ur"</string> <string name="confirmation_title" msgid="4751119145078041732">"Angiv <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> til administration af: <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> er nødvendig for at administrere: <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Ja"</string> <string name="consent_no" msgid="1335543792857823917">"Nej tak"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml index 4867a8ed98e6..60de2ffe0572 100644 --- a/packages/CompanionDeviceManager/res/values-el/strings.xml +++ b/packages/CompanionDeviceManager/res/values-el/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Διαχείριση συνοδευτικής εφαρμογής"</string> <string name="chooser_title" msgid="2262294130493605839">"Επιλέξτε ένα προφίλ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> για διαχείριση από την εφαρμογή <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"συσκευή"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"ρολόι"</string> <string name="confirmation_title" msgid="4751119145078041732">"Ορίστε μια εφαρμογή <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> για διαχείριση του προφίλ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> απαιτείται για τη διαχείριση του προφίλ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Ναι"</string> <string name="consent_no" msgid="1335543792857823917">"Όχι, ευχαριστώ"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml index a0e50917fdc1..2fed1ae7fec3 100644 --- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"watch"</string> <string name="confirmation_title" msgid="4751119145078041732">"Set <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Yes"</string> <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml index a0e50917fdc1..2fed1ae7fec3 100644 --- a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"watch"</string> <string name="confirmation_title" msgid="4751119145078041732">"Set <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Yes"</string> <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml index a0e50917fdc1..2fed1ae7fec3 100644 --- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"watch"</string> <string name="confirmation_title" msgid="4751119145078041732">"Set <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Yes"</string> <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml index a0e50917fdc1..2fed1ae7fec3 100644 --- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"watch"</string> <string name="confirmation_title" msgid="4751119145078041732">"Set <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Yes"</string> <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml index 5780fb407524..4fbb57ed9440 100644 --- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml +++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Administrador de dispositivo complementario"</string> <string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para que <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> lo administre"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"reloj"</string> <string name="confirmation_title" msgid="4751119145078041732">"Configura <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> para administrar <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Se requiere <xliff:g id="APP_NAME">%1$s</xliff:g> para administrar tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Sí"</string> <string name="consent_no" msgid="1335543792857823917">"No, gracias"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml index 78cfa5a74a67..5ca9305ce4d6 100644 --- a/packages/CompanionDeviceManager/res/values-es/strings.xml +++ b/packages/CompanionDeviceManager/res/values-es/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Gestor de dispositivos complementario"</string> <string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para gestionarlo con <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"reloj"</string> <string name="confirmation_title" msgid="4751119145078041732">"Haz que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gestione tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Se necesita <xliff:g id="APP_NAME">%1$s</xliff:g> para gestionar tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Sí"</string> <string name="consent_no" msgid="1335543792857823917">"No, gracias"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml index 3f29da0caaa0..357f05237b85 100644 --- a/packages/CompanionDeviceManager/res/values-et/strings.xml +++ b/packages/CompanionDeviceManager/res/values-et/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Kaasseadme haldur"</string> <string name="chooser_title" msgid="2262294130493605839">"Valige seade <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, mida haldab rakendus <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"seade"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"käekell"</string> <string name="confirmation_title" msgid="4751119145078041732">"Määrake rakendus <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> haldama teie seadet <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> on vajalik teie seadme <xliff:g id="PROFILE_NAME">%2$s</xliff:g> haldamiseks. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Jah"</string> <string name="consent_no" msgid="1335543792857823917">"Tänan, ei"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml index 30907a2fabf7..14c7154acdd3 100644 --- a/packages/CompanionDeviceManager/res/values-eu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Gailu osagarriaren kudeatzailea"</string> <string name="chooser_title" msgid="2262294130493605839">"Aukeratu <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> aplikazioak kudeatu beharreko <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"gailua"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"erlojua"</string> <string name="confirmation_title" msgid="4751119145078041732">"Konfiguratu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (<strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>) kudea dezan"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> erabili behar duzu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> kudeatzeko. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Bai"</string> <string name="consent_no" msgid="1335543792857823917">"Ez"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml index 9e1ecb195f61..6bb9620f09f7 100644 --- a/packages/CompanionDeviceManager/res/values-fa/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"مدیر دستگاه مرتبط"</string> <string name="chooser_title" msgid="2262294130493605839">"انتخاب <xliff:g id="PROFILE_NAME">%1$s</xliff:g> برای مدیریت کردن با <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"دستگاه"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"ساعت"</string> <string name="confirmation_title" msgid="4751119145078041732">"تنظیم <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> برای مدیریت کردن <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"برای مدیریت کردن <xliff:g id="PROFILE_NAME">%2$s</xliff:g> به <xliff:g id="APP_NAME">%1$s</xliff:g> نیاز دارید. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"بله"</string> <string name="consent_no" msgid="1335543792857823917">"نه متشکرم"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml index 4a1f13fd9346..5a9c1cd66aa8 100644 --- a/packages/CompanionDeviceManager/res/values-fi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"Valitse <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, jota <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> hallinnoi"</string> <string name="profile_name_generic" msgid="6851028682723034988">"laite"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"kello"</string> <string name="confirmation_title" msgid="4751119145078041732">"Aseta <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> profiilin (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) hallinnoijaksi – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> tarvitaan profiilin (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) hallinnointiin. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Kyllä"</string> <string name="consent_no" msgid="1335543792857823917">"Ei kiitos"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml index ce5e262d867a..b31babda1a00 100644 --- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Gestionnaire d\'appareil compagnon"</string> <string name="chooser_title" msgid="2262294130493605839">"Choisissez un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré par <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"montre"</string> <string name="confirmation_title" msgid="4751119145078041732">"Utiliser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"L\'application <xliff:g id="APP_NAME">%1$s</xliff:g> est requise pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Oui"</string> <string name="consent_no" msgid="1335543792857823917">"Non merci"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml index e15639b032ec..08c93a2c31c6 100644 --- a/packages/CompanionDeviceManager/res/values-fr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Gestionnaire d\'appareils associés"</string> <string name="chooser_title" msgid="2262294130493605839">"Sélectionner le/la <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré(e) par <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"montre"</string> <string name="confirmation_title" msgid="4751119145078041732">"Définir <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pour la gestion de votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> est requis pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Oui"</string> <string name="consent_no" msgid="1335543792857823917">"Non, merci"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml index 1205e46933ac..c95b90e73edc 100644 --- a/packages/CompanionDeviceManager/res/values-gl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Xestor de dispositivos complementarios"</string> <string name="chooser_title" msgid="2262294130493605839">"Escolle un perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) para que o xestione a aplicación <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"reloxo"</string> <string name="confirmation_title" msgid="4751119145078041732">"Define a aplicación <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> para a xestión do teu perfil (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>): <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Necesítase a aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> para xestionar o teu perfil (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Si"</string> <string name="consent_no" msgid="1335543792857823917">"Non, grazas"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml index 80c0d953f6d0..ac95cc620bfa 100644 --- a/packages/CompanionDeviceManager/res/values-hi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"सहयोगी डिवाइस मैनेजर"</string> <string name="chooser_title" msgid="2262294130493605839">"कोई <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चुनें, ताकि उसे <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> की मदद से प्रबंधित किया जा सके"</string> <string name="profile_name_generic" msgid="6851028682723034988">"डिवाइस"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"स्मार्टवॉच"</string> <string name="confirmation_title" msgid="4751119145078041732">"अपने <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> को प्रबंधित करने के लिए, <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> को सेट करें"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"आपके <xliff:g id="PROFILE_NAME">%2$s</xliff:g> को प्रबंधित करने के लिए, <xliff:g id="APP_NAME">%1$s</xliff:g> की ज़रूरत है. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"हां"</string> <string name="consent_no" msgid="1335543792857823917">"नहीं, रहने दें"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml index b574ff690ac1..df8451f5f127 100644 --- a/packages/CompanionDeviceManager/res/values-hr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"satom"</string> <string name="confirmation_title" msgid="4751119145078041732">"Postavite aplikaciju <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da upravlja vašim profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g> –- <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> potrebna je za upravljanje vašim <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Da"</string> <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml index b271b122a58e..ff1c6c54d7ba 100644 --- a/packages/CompanionDeviceManager/res/values-hu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Társeszközök kezelője"</string> <string name="chooser_title" msgid="2262294130493605839">"A(z) <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> alkalmazással kezelni kívánt <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiválasztása"</string> <string name="profile_name_generic" msgid="6851028682723034988">"eszköz"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"óra"</string> <string name="confirmation_title" msgid="4751119145078041732">"A(z) <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> alkalmazás beállítása a(z) <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (<strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>) kezelésére"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Szükség van a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazásra a(z) <xliff:g id="PROFILE_NAME">%2$s</xliff:g> kezeléséhez. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Igen"</string> <string name="consent_no" msgid="1335543792857823917">"Nem"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml index e8656907bf9a..194223d1d416 100644 --- a/packages/CompanionDeviceManager/res/values-hy/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"Ընտրեք <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ը, որը պետք է կառավարվի <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> հավելվածի կողմից"</string> <string name="profile_name_generic" msgid="6851028682723034988">"սարք"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"ժամացույց"</string> <string name="confirmation_title" msgid="4751119145078041732">"Ընտրեք <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածը որպես <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ի կառավարիչ․ <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Ձեր <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ը կառավարելու համար անհրաժեշտ է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը։ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Այո"</string> <string name="consent_no" msgid="1335543792857823917">"Ոչ, շնորհակալություն"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml index 2d4fd4a5e6e4..58bf3cb4bca4 100644 --- a/packages/CompanionDeviceManager/res/values-in/strings.xml +++ b/packages/CompanionDeviceManager/res/values-in/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Pengelola Perangkat Pendamping"</string> <string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk dikelola oleh <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"perangkat"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"smartwatch"</string> <string name="confirmation_title" msgid="4751119145078041732">"Tetapkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> untuk mengelola <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Perlu <xliff:g id="APP_NAME">%1$s</xliff:g> untuk mengelola <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Ya"</string> <string name="consent_no" msgid="1335543792857823917">"Tidak"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml index 8698bfb5ea35..cc5b98939b71 100644 --- a/packages/CompanionDeviceManager/res/values-is/strings.xml +++ b/packages/CompanionDeviceManager/res/values-is/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Stjórnun fylgdartækja"</string> <string name="chooser_title" msgid="2262294130493605839">"Velja <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sem <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> á að stjórna"</string> <string name="profile_name_generic" msgid="6851028682723034988">"tæki"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"úr"</string> <string name="confirmation_title" msgid="4751119145078041732">"Veita <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> stjórn á <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> er krafist til að stjórna <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Já"</string> <string name="consent_no" msgid="1335543792857823917">"Nei, takk"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml index beb1482f77c3..4cbefd801187 100644 --- a/packages/CompanionDeviceManager/res/values-it/strings.xml +++ b/packages/CompanionDeviceManager/res/values-it/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Gestione dispositivi companion"</string> <string name="chooser_title" msgid="2262294130493605839">"Scegli un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> che sia gestito da <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"orologio"</string> <string name="confirmation_title" msgid="4751119145078041732">"Configura <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> per gestire il tuo <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"È richiesta l\'app <xliff:g id="APP_NAME">%1$s</xliff:g> per gestire il tuo <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Sì"</string> <string name="consent_no" msgid="1335543792857823917">"No, grazie"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml index b50c56a33ebf..b695d9ded477 100644 --- a/packages/CompanionDeviceManager/res/values-ja/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"コンパニオン デバイス マネージャ"</string> <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> の管理対象となる <xliff:g id="PROFILE_NAME">%1$s</xliff:g> の選択"</string> <string name="profile_name_generic" msgid="6851028682723034988">"デバイス"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"ウォッチ"</string> <string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> で <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> を管理するよう設定する"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> を管理するために <xliff:g id="APP_NAME">%1$s</xliff:g> が必要です。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"はい"</string> <string name="consent_no" msgid="1335543792857823917">"いいえ"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml index 4ab0abdf4ac8..300c94f63cbe 100644 --- a/packages/CompanionDeviceManager/res/values-ka/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"კომპანიონი მოწყობილობების მენეჯერი"</string> <string name="chooser_title" msgid="2262294130493605839">"აირჩიეთ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, რომელიც უნდა მართოს <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>-მა"</string> <string name="profile_name_generic" msgid="6851028682723034988">"მოწყობილობა"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"საათი"</string> <string name="confirmation_title" msgid="4751119145078041732">"დააყენეთ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>, რომ მართოს თქვენი <xliff:g id="PROFILE_NAME">%2$s</xliff:g> — <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"თქვენი <xliff:g id="PROFILE_NAME">%2$s</xliff:g>-ის სამართავად საჭიროა <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"დიახ"</string> <string name="consent_no" msgid="1335543792857823917">"არა, გმადლობთ"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml index fcfe030ddb0e..94d6c3ed2d5f 100644 --- a/packages/CompanionDeviceManager/res/values-kk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> арқылы басқарылатын <xliff:g id="PROFILE_NAME">%1$s</xliff:g> құрылғысын таңдаңыз"</string> <string name="profile_name_generic" msgid="6851028682723034988">"құрылғы"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"сағат"</string> <string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> қолданбасына <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) құрылғысын басқаруға рұқсат беру"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> құрылғысын басқару үшін <xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Иә"</string> <string name="consent_no" msgid="1335543792857823917">"Жоқ, рақмет"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml index e70800445139..db13fe7884af 100644 --- a/packages/CompanionDeviceManager/res/values-km/strings.xml +++ b/packages/CompanionDeviceManager/res/values-km/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"កម្មវិធីគ្រប់គ្រងឧបករណ៍ដៃគូ"</string> <string name="chooser_title" msgid="2262294130493605839">"ជ្រើសរើស <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ដើម្បីឱ្យស្ថិតក្រោមការគ្រប់គ្រងរបស់ <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ឧបករណ៍"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"នាឡិកា"</string> <string name="confirmation_title" msgid="4751119145078041732">"កំណត់ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%2$s</xliff:g> របស់អ្នក - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"ចាំបាច់ត្រូវមាន <xliff:g id="APP_NAME">%1$s</xliff:g> ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%2$s</xliff:g> របស់អ្នក។ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"បាទ/ចាស"</string> <string name="consent_no" msgid="1335543792857823917">"ទេ អរគុណ"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml index 299d586045e8..1363e57e39a3 100644 --- a/packages/CompanionDeviceManager/res/values-ko/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"부속 기기 관리자"</string> <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>에서 관리할 <xliff:g id="PROFILE_NAME">%1$s</xliff:g>을(를) 선택"</string> <string name="profile_name_generic" msgid="6851028682723034988">"기기"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"시계"</string> <string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>에서 <xliff:g id="PROFILE_NAME">%2$s</xliff:g>(<strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>)을(를) 관리하도록 설정"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> 프로필을 관리하려면 <xliff:g id="APP_NAME">%1$s</xliff:g> 앱이 필요합니다. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"예"</string> <string name="consent_no" msgid="1335543792857823917">"취소"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml index 35ee34db59f7..c01e2350aa04 100644 --- a/packages/CompanionDeviceManager/res/values-ky/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> тарабынан башкарылсын"</string> <string name="profile_name_generic" msgid="6851028682723034988">"түзмөк"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"саат"</string> <string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> түзмөгүңүздү башкарсын"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> профилиңизди башкаруу үчүн <xliff:g id="APP_NAME">%1$s</xliff:g> керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Ооба"</string> <string name="consent_no" msgid="1335543792857823917">"Жок, рахмат"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml index a27bc15cc850..68218dd79c50 100644 --- a/packages/CompanionDeviceManager/res/values-lo/strings.xml +++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"ຕົວຈັດການອຸປະກອນປະກອບ"</string> <string name="chooser_title" msgid="2262294130493605839">"ເລືອກ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ເພື່ອໃຫ້ຖືກຈັດການໂດຍ <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ອຸປະກອນ"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"ໂມງ"</string> <string name="confirmation_title" msgid="4751119145078041732">"ຕັ້ງ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> ຂອງທ່ານ"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"ຕ້ອງໃຊ້ <xliff:g id="APP_NAME">%1$s</xliff:g> ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ຂອງທ່ານ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"ແມ່ນແລ້ວ"</string> <string name="consent_no" msgid="1335543792857823917">"ບໍ່, ຂອບໃຈ"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml index 11510acad919..5fd8280affca 100644 --- a/packages/CompanionDeviceManager/res/values-lt/strings.xml +++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"Jūsų <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, kurį valdys <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> (pasirinkite)"</string> <string name="profile_name_generic" msgid="6851028682723034988">"įrenginys"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"laikrodis"</string> <string name="confirmation_title" msgid="4751119145078041732">"Nustatyti, kad <xliff:g id="PROFILE_NAME">%2$s</xliff:g> <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> būtų valdomas programos <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Norint valdyti jūsų <xliff:g id="PROFILE_NAME">%2$s</xliff:g>, reikalinga programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Taip"</string> <string name="consent_no" msgid="1335543792857823917">"Ne, ačiū"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml index 6a85c836bb49..bf036ec70d73 100644 --- a/packages/CompanionDeviceManager/res/values-lv/strings.xml +++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Palīgierīču pārzinis"</string> <string name="chooser_title" msgid="2262294130493605839">"Profila (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) izvēle, ko pārvaldīt lietotnē <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ierīce"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"pulkstenis"</string> <string name="confirmation_title" msgid="4751119145078041732">"Lietotnes <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> iestatīšana profila (<xliff:g id="PROFILE_NAME">%2$s</xliff:g> — <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>) pārvaldībai"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Lai pārvaldītu profilu (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>), nepieciešama lietotne <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Jā"</string> <string name="consent_no" msgid="1335543792857823917">"Nē, paldies"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml index 6fed96bcdf6d..427ca8f940a0 100644 --- a/packages/CompanionDeviceManager/res/values-mk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"Изберете <xliff:g id="PROFILE_NAME">%1$s</xliff:g> со којшто ќе управува <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"уред"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"часовник"</string> <string name="confirmation_title" msgid="4751119145078041732">"Поставете ја <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да управува со <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> е потребна за да управува со <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Да"</string> <string name="consent_no" msgid="1335543792857823917">"Не, фала"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml index 6b071cd54950..a48c45f73ae7 100644 --- a/packages/CompanionDeviceManager/res/values-ml/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"കമ്പാനിയൻ ഉപകരണ മാനേജർ"</string> <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ഉപയോഗിച്ച് മാനേജ് ചെയ്യുന്നതിന് ഒരു <xliff:g id="PROFILE_NAME">%1$s</xliff:g> തിരഞ്ഞെടുക്കുക"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ഉപകരണം"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"വാച്ച്"</string> <string name="confirmation_title" msgid="4751119145078041732">"നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> മാനേജ് ചെയ്യുന്നതിന് <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> സജ്ജീകരിക്കുക - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്ന ആപ്പിന് നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> മാനേജ് ചെയ്യേണ്ടതുണ്ട്. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"വേണം"</string> <string name="consent_no" msgid="1335543792857823917">"വേണ്ട"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml index 4d048cb81ad9..7ac20e613185 100644 --- a/packages/CompanionDeviceManager/res/values-mn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>-н удирдах<xliff:g id="PROFILE_NAME">%1$s</xliff:g>-г сонгоно уу"</string> <string name="profile_name_generic" msgid="6851028682723034988">"төхөөрөмж"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"цаг"</string> <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>-аа удирдахын тулд <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-г тохируулна уу - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Таны <xliff:g id="PROFILE_NAME">%2$s</xliff:g>-г удирдахын тулд <xliff:g id="APP_NAME">%1$s</xliff:g> шаардлагатай. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Тийм"</string> <string name="consent_no" msgid="1335543792857823917">"Үгүй, баярлалаа"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml index b16869dece36..9c2783cdcbbb 100644 --- a/packages/CompanionDeviceManager/res/values-my/strings.xml +++ b/packages/CompanionDeviceManager/res/values-my/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"တွဲဖက်ကိရိယာ မန်နေဂျာ"</string> <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> က စီမံခန့်ခွဲရန် <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို ရွေးချယ်ပါ"</string> <string name="profile_name_generic" msgid="6851028682723034988">"စက်"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"နာရီ"</string> <string name="confirmation_title" msgid="4751119145078041732">"သင်၏ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> ကို စီမံခန့်ခွဲရန် <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ကို သတ်မှတ်ပါ"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"သင်၏ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ကို စီမံခန့်ခွဲရန် <xliff:g id="APP_NAME">%1$s</xliff:g> ကို လိုအပ်ပါသည်။ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Yes"</string> <string name="consent_no" msgid="1335543792857823917">"မလိုပါ"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml index cac087f45d69..26fbb0350edc 100644 --- a/packages/CompanionDeviceManager/res/values-nb/strings.xml +++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"Velg <xliff:g id="PROFILE_NAME">%1$s</xliff:g> som skal administreres av <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"klokke"</string> <string name="confirmation_title" msgid="4751119145078041732">"Angi <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> for å administrere <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> kreves for å administrere <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Ja"</string> <string name="consent_no" msgid="1335543792857823917">"Nei takk"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml index e390161bb22a..f289b3780672 100644 --- a/packages/CompanionDeviceManager/res/values-ne/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"सहयोगी यन्त्रको प्रबन्धक"</string> <string name="chooser_title" msgid="2262294130493605839">"आफूले <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> प्रयोग गरी व्यवस्थापन गर्न चाहेको <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चयन गर्नुहोस्"</string> <string name="profile_name_generic" msgid="6851028682723034988">"यन्त्र"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"घडी"</string> <string name="confirmation_title" msgid="4751119145078041732">"आफ्नो <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> व्यवस्थापन गर्न <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> तोक्नुहोस्"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापन गर्न <xliff:g id="APP_NAME">%1$s</xliff:g> इन्स्टल गर्नु पर्ने हुन्छ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"अँ"</string> <string name="consent_no" msgid="1335543792857823917">"सहमत छुइनँ"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml index 2153770e169d..0c9cdffd4e17 100644 --- a/packages/CompanionDeviceManager/res/values-nl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"Een <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiezen om te beheren met <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"apparaat"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"horloge"</string> <string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> instellen om je <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> te beheren"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Je hebt <xliff:g id="APP_NAME">%1$s</xliff:g> nodig om je <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te beheren. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Ja"</string> <string name="consent_no" msgid="1335543792857823917">"Nee, bedankt"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml index b16d0458055a..b07af57a936a 100644 --- a/packages/CompanionDeviceManager/res/values-pl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Menedżer urządzeń towarzyszących"</string> <string name="chooser_title" msgid="2262294130493605839">"Wybierz profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, którym ma zarządzać aplikacja <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"urządzenie"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"zegarek"</string> <string name="confirmation_title" msgid="4751119145078041732">"Ustaw aplikację <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> do zarządzania profilem <xliff:g id="PROFILE_NAME">%2$s</xliff:g> na urządzeniu <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest wymagana do zarządzania profilem <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Tak"</string> <string name="consent_no" msgid="1335543792857823917">"Nie, dziękuję"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml index 40f9d2d7310e..16906f62f9f1 100644 --- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Gerenciador de dispositivos complementar"</string> <string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string> <string name="confirmation_title" msgid="4751119145078041732">"Defina o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> como gerenciador do seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (<strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>)"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Sim"</string> <string name="consent_no" msgid="1335543792857823917">"Agora não"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml index 40f9d2d7310e..16906f62f9f1 100644 --- a/packages/CompanionDeviceManager/res/values-pt/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Gerenciador de dispositivos complementar"</string> <string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string> <string name="confirmation_title" msgid="4751119145078041732">"Defina o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> como gerenciador do seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (<strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>)"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Sim"</string> <string name="consent_no" msgid="1335543792857823917">"Agora não"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml index 6040adf969f3..187cfbdfe6f0 100644 --- a/packages/CompanionDeviceManager/res/values-ro/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Manager de dispozitiv Companion"</string> <string name="chooser_title" msgid="2262294130493605839">"Alegeți un profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> pe care să îl gestioneze <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispozitiv"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"ceas"</string> <string name="confirmation_title" msgid="4751119145078041732">"Setați <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pentru a vă gestiona profilul <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> este necesară pentru a vă gestiona profilul <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Da"</string> <string name="consent_no" msgid="1335543792857823917">"Nu, mulțumesc"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml index 9939a8255d0b..8dd9a392712b 100644 --- a/packages/CompanionDeviceManager/res/values-ru/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Управление подключенными устройствами"</string> <string name="chooser_title" msgid="2262294130493605839">"Выберите устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), которым будет управлять приложение <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"часы"</string> <string name="confirmation_title" msgid="4751119145078041732">"Разрешите приложению <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> управлять устройством <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>)"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" необходимо для управления устройством (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Да"</string> <string name="consent_no" msgid="1335543792857823917">"Нет"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml index 6e6b0293cfa8..9e7c02e0c0d9 100644 --- a/packages/CompanionDeviceManager/res/values-si/strings.xml +++ b/packages/CompanionDeviceManager/res/values-si/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"සහායක උපාංග කළමනාකරු"</string> <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> මගින් කළමනාකරණය කරනු ලැබීමට <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ක් තෝරන්න"</string> <string name="profile_name_generic" msgid="6851028682723034988">"උපාංගය"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"ඔරලෝසුව"</string> <string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ඔබගේ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> කළමනාකරණය කිරීමට සකසන්න - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"ඔබගේ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> කළමනාකරණය කිරීමට <xliff:g id="APP_NAME">%1$s</xliff:g> අවශ්යයි. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"ඔව්"</string> <string name="consent_no" msgid="1335543792857823917">"එපා, ස්තුතියි"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml index 520aa0900e4d..55a47c2df427 100644 --- a/packages/CompanionDeviceManager/res/values-sk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Správca sprievodných zariadení"</string> <string name="chooser_title" msgid="2262294130493605839">"Vyberte profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ktorý bude spravovať aplikácia <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"zariadenie"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string> <string name="confirmation_title" msgid="4751119145078041732">"Nastavte aplikáciu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>, aby spravovala profil <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Na správu profilu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> je potrebná aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Áno"</string> <string name="consent_no" msgid="1335543792857823917">"Nie, vďaka"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml index f60cab8e4710..159afd543609 100644 --- a/packages/CompanionDeviceManager/res/values-sl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Upravitelj spremljevalnih naprav"</string> <string name="chooser_title" msgid="2262294130493605839">"Izbira naprave <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ki jo bo upravljala aplikacija <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"naprava"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"ura"</string> <string name="confirmation_title" msgid="4751119145078041732">"Nastavitev aplikacije <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>, ki bo upravljala napravo <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Za upravljanje naprave <xliff:g id="PROFILE_NAME">%2$s</xliff:g> potrebujete aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Da"</string> <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml index da08d879940f..fdbbe8e668f5 100644 --- a/packages/CompanionDeviceManager/res/values-sr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Менаџер придруженог уређаја"</string> <string name="chooser_title" msgid="2262294130493605839">"Одаберите профил <xliff:g id="PROFILE_NAME">%1$s</xliff:g> којим ће управљати апликација <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"уређај"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"сат"</string> <string name="confirmation_title" msgid="4751119145078041732">"Подесите апликацију <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да управља профилом <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је неопходна за управљање профилом <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Да"</string> <string name="consent_no" msgid="1335543792857823917">"Не, хвала"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-sv/strings.xml b/packages/CompanionDeviceManager/res/values-sv/strings.xml index a73cbfa85e40..bfd25162aec6 100644 --- a/packages/CompanionDeviceManager/res/values-sv/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"Välj en <xliff:g id="PROFILE_NAME">%1$s</xliff:g> för hantering av <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"klocka"</string> <string name="confirmation_title" msgid="4751119145078041732">"Konfigurera <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> för att hantera din <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> krävs för att hantera din <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Ja"</string> <string name="consent_no" msgid="1335543792857823917">"Nej tack"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml index 5865ec97bbb6..437ae7f332e8 100644 --- a/packages/CompanionDeviceManager/res/values-sw/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Kidhibiti cha Vifaa Visaidizi"</string> <string name="chooser_title" msgid="2262294130493605839">"Chagua <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ili idhibitiwe na <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"kifaa"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"saa"</string> <string name="confirmation_title" msgid="4751119145078041732">"Weka <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ili udhibiti <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yako - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> linahitajika ili kudhibiti <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yako. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Ndiyo"</string> <string name="consent_no" msgid="1335543792857823917">"Hapana"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml index f9d640128e9c..9b4a720a4863 100644 --- a/packages/CompanionDeviceManager/res/values-ta/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"கம்பேனியன் சாதன நிர்வாகி"</string> <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ஆப்ஸ் நிர்வகிக்கக்கூடிய <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ஐத் தேர்ந்தெடுங்கள்"</string> <string name="profile_name_generic" msgid="6851028682723034988">"சாதனம்"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"வாட்ச்"</string> <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> ஐ நிர்வகிக்க <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ஆப்ஸை அமையுங்கள்"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> ஐ நிர்வகிக்க <xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் வேண்டும். <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"ஆம்"</string> <string name="consent_no" msgid="1335543792857823917">"வேண்டாம்"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml index 82616cc8bc19..6e785de583aa 100644 --- a/packages/CompanionDeviceManager/res/values-te/strings.xml +++ b/packages/CompanionDeviceManager/res/values-te/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"సహచర పరికర మేనేజర్"</string> <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ద్వారా మేనేజ్ చేయబడటానికి ఒక <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ను ఎంచుకోండి"</string> <string name="profile_name_generic" msgid="6851028682723034988">"పరికరం"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"వాచ్"</string> <string name="confirmation_title" msgid="4751119145078041732">"మీ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>ను మేనేజ్ చేయడానికి <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ను సెటప్ చేయండి"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"మీ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ను మేనేజ్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> అవసరం ఉంది. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"అవును"</string> <string name="consent_no" msgid="1335543792857823917">"వద్దు, ధన్యవాదాలు"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml index 77a9fff1f2fc..b727d42035dd 100644 --- a/packages/CompanionDeviceManager/res/values-th/strings.xml +++ b/packages/CompanionDeviceManager/res/values-th/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"เลือก<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ที่จะให้มีการจัดการโดย <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"อุปกรณ์"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"นาฬิกา"</string> <string name="confirmation_title" msgid="4751119145078041732">"ตั้งค่า <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ให้จัดการ<xliff:g id="PROFILE_NAME">%2$s</xliff:g>ของคุณ - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"ต้องใช้ <xliff:g id="APP_NAME">%1$s</xliff:g> ในการจัดการ<xliff:g id="PROFILE_NAME">%2$s</xliff:g> <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"ใช่"</string> <string name="consent_no" msgid="1335543792857823917">"ไม่เป็นไร"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml index 6d6264236e46..a93282a8fae0 100644 --- a/packages/CompanionDeviceManager/res/values-tl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Kasamang Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"Pumili ng <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para pamahalaan ng <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"relo"</string> <string name="confirmation_title" msgid="4751119145078041732">"Itakda ang <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Kailangan ang <xliff:g id="APP_NAME">%1$s</xliff:g> para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Oo"</string> <string name="consent_no" msgid="1335543792857823917">"Huwag na lang"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml index 2c30ea743d8c..3abe064d60bc 100644 --- a/packages/CompanionDeviceManager/res/values-tr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> tarafından yönetilecek bir <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string> <string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"saat"</string> <string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> uygulamasını, <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> cihazınızı yönetecek şekilde ayarlayın"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yönetimi için gereklidir. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Evet"</string> <string name="consent_no" msgid="1335543792857823917">"Hayır, teşekkürler"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml index f68fd9230c71..161d95e127e8 100644 --- a/packages/CompanionDeviceManager/res/values-uk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Диспетчер супутніх пристроїв"</string> <string name="chooser_title" msgid="2262294130493605839">"Виберіть <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, яким керуватиме додаток <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"пристрій"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"годинник"</string> <string name="confirmation_title" msgid="4751119145078041732">"Налаштуйте додаток <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>, щоб керувати своїм пристроєм <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>)"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Щоб керувати своїм пристроєм (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>), вам потрібен додаток <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Так"</string> <string name="consent_no" msgid="1335543792857823917">"Ні, дякую"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml index c8c4b22c51df..4cce2e8b7eb1 100644 --- a/packages/CompanionDeviceManager/res/values-uz/strings.xml +++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> boshqaradigan <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qurilmasini tanlang"</string> <string name="profile_name_generic" msgid="6851028682723034988">"qurilma"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"tomosha qilish"</string> <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> qurilmalarini boshqarish uchun <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ilovasini sozlang"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="PROFILE_NAME">%2$s</xliff:g> qurilmasini boshqarish uchun kerak. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Ha"</string> <string name="consent_no" msgid="1335543792857823917">"Kerak emas"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml index f0234a4e7d9f..06a1ab6846ae 100644 --- a/packages/CompanionDeviceManager/res/values-vi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> <string name="chooser_title" msgid="2262294130493605839">"Chọn một <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sẽ do <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> quản lý"</string> <string name="profile_name_generic" msgid="6851028682723034988">"thiết bị"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"đồng hồ"</string> <string name="confirmation_title" msgid="4751119145078041732">"Đặt <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> để quản lý <xliff:g id="PROFILE_NAME">%2$s</xliff:g> của bạn – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"Cần có <xliff:g id="APP_NAME">%1$s</xliff:g> để quản lý <xliff:g id="PROFILE_NAME">%2$s</xliff:g> của bạn. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Có"</string> <string name="consent_no" msgid="1335543792857823917">"Không, cảm ơn"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml index caf232169267..12bfcf3629cd 100644 --- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"配套设备管理器"</string> <string name="chooser_title" msgid="2262294130493605839">"选择要由<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"设备"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"手表"</string> <string name="confirmation_title" msgid="4751119145078041732">"设为由<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>管理您的<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"若要管理<xliff:g id="PROFILE_NAME">%2$s</xliff:g>,您需要使用<xliff:g id="APP_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"好"</string> <string name="consent_no" msgid="1335543792857823917">"不用了"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml index 5ec64a513844..0c583b211035 100644 --- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"隨附裝置管理員"</string> <string name="chooser_title" msgid="2262294130493605839">"選擇由 <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> 管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"手錶"</string> <string name="confirmation_title" msgid="4751119145078041732">"設定 <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 來管理您的 <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"必須使用 <xliff:g id="APP_NAME">%1$s</xliff:g> 來管理您的<xliff:g id="PROFILE_NAME">%2$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"是"</string> <string name="consent_no" msgid="1335543792857823917">"不用了,謝謝"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml index b8c2ed0f952a..519f0e8a7082 100644 --- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"隨附裝置管理員"</string> <string name="chooser_title" msgid="2262294130493605839">"選擇要讓「<xliff:g id="APP_NAME">%2$s</xliff:g>」<strong></strong>管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"手錶"</string> <string name="confirmation_title" msgid="4751119145078041732">"授權讓「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>管理你的<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"如要管理你的<xliff:g id="PROFILE_NAME">%2$s</xliff:g>,必須使用「<xliff:g id="APP_NAME">%1$s</xliff:g>」。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"是"</string> <string name="consent_no" msgid="1335543792857823917">"不用了,謝謝"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml index 7df0869f19c7..7721b54166f5 100644 --- a/packages/CompanionDeviceManager/res/values-zu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml @@ -19,11 +19,9 @@ <string name="app_label" msgid="4470785958457506021">"Isiphathi sedivayisi esihambisanayo"</string> <string name="chooser_title" msgid="2262294130493605839">"Khetha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> ezophathwa yi-<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"idivayisi"</string> - <!-- no translation found for profile_name_watch (576290739483672360) --> - <skip /> + <string name="profile_name_watch" msgid="576290739483672360">"buka"</string> <string name="confirmation_title" msgid="4751119145078041732">"Setha i-<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ukuba iphathe i-<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string> - <!-- no translation found for profile_summary (2009764182871566255) --> - <skip /> + <string name="profile_summary" msgid="2009764182871566255">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> iyadingeka ukuphatha i-<xliff:g id="PROFILE_NAME">%2$s</xliff:g> yakho. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string> <string name="consent_yes" msgid="4055438216605487056">"Yebo"</string> <string name="consent_no" msgid="1335543792857823917">"Cha ngiyabonga"</string> </resources> diff --git a/packages/SettingsLib/res/values-af/arrays.xml b/packages/SettingsLib/res/values-af/arrays.xml index 93f00e7df169..ca409f379788 100644 --- a/packages/SettingsLib/res/values-af/arrays.xml +++ b/packages/SettingsLib/res/values-af/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktief (media)"</item> <item msgid="5001852592115448348">", aktief (foon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Af"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Af"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Af"</item> - <item msgid="4195153527464162486">"64 K per logbuffer"</item> - <item msgid="7464037639415220106">"256 K per logbuffer"</item> - <item msgid="8539423820514360724">"1 M per logbuffer"</item> - <item msgid="1984761927103140651">"4 M per logbuffer"</item> - <item msgid="7892098981256010498">"16 M per logbuffer"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Af"</item> <item msgid="6014837961827347618">"Alles"</item> diff --git a/packages/SettingsLib/res/values-am/arrays.xml b/packages/SettingsLib/res/values-am/arrays.xml index 18696b1b2c5c..e941c1167af8 100644 --- a/packages/SettingsLib/res/values-am/arrays.xml +++ b/packages/SettingsLib/res/values-am/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">"፣ ገቢር (ሚዲያ)"</item> <item msgid="5001852592115448348">"፣ ገቢር (ስልክ)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"ጠፍቷል"</item> - <item msgid="7839165897132179888">"64 ኪባ"</item> - <item msgid="2715700596495505626">"256 ኪባ"</item> - <item msgid="7099386891713159947">"1 ሜባ"</item> - <item msgid="6069075827077845520">"4 ሜባ"</item> - <item msgid="8243549501764402572">"16 ሜባ"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"ጠፍቷል"</item> <item msgid="4064786181089783077">"64 ኪባ"</item> <item msgid="3052710745383602630">"256 ኪባ"</item> <item msgid="3691785423374588514">"1 ሜባ"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"ጠፍቷል"</item> - <item msgid="4195153527464162486">"64 ኪባ በምዝግብ ማስታወሻ ቋጥ"</item> - <item msgid="7464037639415220106">"256 ኪባ በምዝግብ ማስታወሻ ቋጥ"</item> - <item msgid="8539423820514360724">"1 ሜ በምዝግብ ማስታወሻ ቋጥ"</item> - <item msgid="1984761927103140651">"4 ሜ በምዝግብ ማስታወሻ ቋጥ"</item> - <item msgid="7892098981256010498">"16 ሜ በምዝግብ ማስታወሻ ቋጥ"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"ጠፍቷል"</item> <item msgid="6014837961827347618">"ሁሉም"</item> diff --git a/packages/SettingsLib/res/values-ar/arrays.xml b/packages/SettingsLib/res/values-ar/arrays.xml index d09b50e08efb..db1d4b4b52b2 100644 --- a/packages/SettingsLib/res/values-ar/arrays.xml +++ b/packages/SettingsLib/res/values-ar/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">"، مُفعَّل (وسائط)"</item> <item msgid="5001852592115448348">"، مُفعَّل (هاتف)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"إيقاف"</item> - <item msgid="7839165897132179888">"٦٤ كيلوبايت"</item> - <item msgid="2715700596495505626">"٢٥٦ كيلوبايت"</item> - <item msgid="7099386891713159947">"1 ميغابايت"</item> - <item msgid="6069075827077845520">"٤ ميغابايت"</item> - <item msgid="8243549501764402572">"١٦ ميغابايت"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"إيقاف"</item> <item msgid="4064786181089783077">"٦٤ كيلوبايت"</item> <item msgid="3052710745383602630">"٢٥٦ كيلوبايت"</item> <item msgid="3691785423374588514">"1 ميغابايت"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"إيقاف"</item> - <item msgid="4195153527464162486">"٦٤ كيلوبايت لكل ذاكرة تخزين مؤقت للتسجيل"</item> - <item msgid="7464037639415220106">"٢٥٦ كيلوبايت لكل ذاكرة تخزين مؤقت للتسجيل"</item> - <item msgid="8539423820514360724">"1 ميغابايت لكل ذاكرة تخزين مؤقت للتسجيل"</item> - <item msgid="1984761927103140651">"٤ ميغابايت لكل ذاكرة تخزين مؤقت للتسجيل"</item> - <item msgid="7892098981256010498">"١٦ ميغابايت لكل ذاكرة تخزين مؤقت للتسجيل"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"إيقاف"</item> <item msgid="6014837961827347618">"الكل"</item> diff --git a/packages/SettingsLib/res/values-as/arrays.xml b/packages/SettingsLib/res/values-as/arrays.xml index b619d3be9902..b2494fb13b17 100644 --- a/packages/SettingsLib/res/values-as/arrays.xml +++ b/packages/SettingsLib/res/values-as/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", সক্ৰিয় (মিডিয়া)"</item> <item msgid="5001852592115448348">", সক্ৰিয় (ফ\'ন)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"অফ কৰক"</item> - <item msgid="7839165897132179888">"৬৪কে."</item> - <item msgid="2715700596495505626">"২৫৬কে."</item> - <item msgid="7099386891713159947">"১মি."</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"১৬মি."</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"অফ কৰক"</item> <item msgid="4064786181089783077">"৬৪কে."</item> <item msgid="3052710745383602630">"২৫৬কে."</item> <item msgid="3691785423374588514">"১মি."</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"অফ কৰক"</item> - <item msgid="4195153527464162486">"প্ৰতিটো লগ বাফাৰত ৬৪কে."</item> - <item msgid="7464037639415220106">"প্ৰতি লগ বাফাৰত 256K"</item> - <item msgid="8539423820514360724">"প্ৰতিটো লগ বাফাৰত ১মি."</item> - <item msgid="1984761927103140651">"প্ৰতিটো লগ বাফাৰত ৪মি."</item> - <item msgid="7892098981256010498">"প্ৰতিটো লগ বাফাৰত ১৬মি."</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"অফ অৱস্থাত আছে"</item> <item msgid="6014837961827347618">"সকলো"</item> diff --git a/packages/SettingsLib/res/values-az/arrays.xml b/packages/SettingsLib/res/values-az/arrays.xml index 55ec9d8c6b00..6ee2b8f69d57 100644 --- a/packages/SettingsLib/res/values-az/arrays.xml +++ b/packages/SettingsLib/res/values-az/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktiv (media)"</item> <item msgid="5001852592115448348">", aktiv (telefon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Deaktiv"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Deaktiv"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Deaktiv"</item> - <item msgid="4195153527464162486">"hər jurnal buferinə 64K"</item> - <item msgid="7464037639415220106">"hər jurnal buferinə 256K"</item> - <item msgid="8539423820514360724">"hər jurnal buferinə 1M"</item> - <item msgid="1984761927103140651">"hər jurnal buferinə 4M"</item> - <item msgid="7892098981256010498">"hər jurnal buferinə 16M"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Deaktiv"</item> <item msgid="6014837961827347618">"Bütün"</item> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml index 2926067e1afc..630ad7de5e38 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktivan (medijski)"</item> <item msgid="5001852592115448348">", aktivan (telefon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Isključeno"</item> - <item msgid="7839165897132179888">"64 kB"</item> - <item msgid="2715700596495505626">"256 kB"</item> - <item msgid="7099386891713159947">"1 MB"</item> - <item msgid="6069075827077845520">"4 MB"</item> - <item msgid="8243549501764402572">"16 MB"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Isključeno"</item> <item msgid="4064786181089783077">"64 kB"</item> <item msgid="3052710745383602630">"256 kB"</item> <item msgid="3691785423374588514">"1 MB"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Isključeno"</item> - <item msgid="4195153527464162486">"64 kB po međumemoriji evidencije"</item> - <item msgid="7464037639415220106">"256 kB po međumemoriji evidencije"</item> - <item msgid="8539423820514360724">"1 MB po međumemoriji evidencije"</item> - <item msgid="1984761927103140651">"4 MB po međumemoriji evidencije"</item> - <item msgid="7892098981256010498">"16 MB po međumemoriji evidencije"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Isključeno"</item> <item msgid="6014837961827347618">"Sve"</item> diff --git a/packages/SettingsLib/res/values-be/arrays.xml b/packages/SettingsLib/res/values-be/arrays.xml index af3a161403e9..2d2c509ceb03 100644 --- a/packages/SettingsLib/res/values-be/arrays.xml +++ b/packages/SettingsLib/res/values-be/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", уключана (мультымедыя)"</item> <item msgid="5001852592115448348">", уключана (тэлефон)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Выкл."</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Выкл."</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Выкл."</item> - <item msgid="4195153527464162486">"64K на буфер журнала"</item> - <item msgid="7464037639415220106">"256K на буфер журнала"</item> - <item msgid="8539423820514360724">"1M на буфер журнала"</item> - <item msgid="1984761927103140651">"4M на буфер журнала"</item> - <item msgid="7892098981256010498">"16M на буфер журнала"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Выключана"</item> <item msgid="6014837961827347618">"Усе"</item> diff --git a/packages/SettingsLib/res/values-bg/arrays.xml b/packages/SettingsLib/res/values-bg/arrays.xml index 1b25bed71945..482ec2292f5a 100644 --- a/packages/SettingsLib/res/values-bg/arrays.xml +++ b/packages/SettingsLib/res/values-bg/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">"– активно (мултимедия)"</item> <item msgid="5001852592115448348">"– активно (телефон)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Изключено"</item> - <item msgid="7839165897132179888">"64 КБ"</item> - <item msgid="2715700596495505626">"256 КБ"</item> - <item msgid="7099386891713159947">"1 МБ"</item> - <item msgid="6069075827077845520">"4 МБ"</item> - <item msgid="8243549501764402572">"16 МБ"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Изключено"</item> <item msgid="4064786181089783077">"64 КБ"</item> <item msgid="3052710745383602630">"256 КБ"</item> <item msgid="3691785423374588514">"1 МБ"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Изключено"</item> - <item msgid="4195153527464162486">"Рег. буфер – 64 КБ"</item> - <item msgid="7464037639415220106">"Рег. буфер – 256 КБ"</item> - <item msgid="8539423820514360724">"Рег. буфер – 1 МБ"</item> - <item msgid="1984761927103140651">"Рег. буфер – 4 МБ"</item> - <item msgid="7892098981256010498">"Рег. буфер – 16 МБ"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Изкл."</item> <item msgid="6014837961827347618">"Всички"</item> diff --git a/packages/SettingsLib/res/values-bn/arrays.xml b/packages/SettingsLib/res/values-bn/arrays.xml index 34cbc8fda9c2..2da307640be8 100644 --- a/packages/SettingsLib/res/values-bn/arrays.xml +++ b/packages/SettingsLib/res/values-bn/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", চালু আছে (মিডিয়া)"</item> <item msgid="5001852592115448348">", চালু আছে (ফোন)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"বন্ধ আছে"</item> - <item msgid="7839165897132179888">"৬৪K"</item> - <item msgid="2715700596495505626">"২৫৬K"</item> - <item msgid="7099386891713159947">"১M"</item> - <item msgid="6069075827077845520">"৪M"</item> - <item msgid="8243549501764402572">"১৬M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"বন্ধ আছে"</item> <item msgid="4064786181089783077">"৬৪K"</item> <item msgid="3052710745383602630">"২৫৬K"</item> <item msgid="3691785423374588514">"১M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"বন্ধ আছে"</item> - <item msgid="4195153527464162486">"লগ বাফার প্রতি ৬৪K"</item> - <item msgid="7464037639415220106">"লগ বাফার প্রতি ২৫৬K"</item> - <item msgid="8539423820514360724">"লগ বাফার প্রতি ১M"</item> - <item msgid="1984761927103140651">"লগ বাফার প্রতি ৪M"</item> - <item msgid="7892098981256010498">"লগ বাফার প্রতি ১৬M"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"বন্ধ আছে"</item> <item msgid="6014837961827347618">"সমস্ত"</item> diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml index 6d2f1f36f233..b704385675bf 100644 --- a/packages/SettingsLib/res/values-bs/arrays.xml +++ b/packages/SettingsLib/res/values-bs/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktivan (mediji)"</item> <item msgid="5001852592115448348">", aktivan (telefon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Isključeno"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Isključeno"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Isključeno"</item> - <item msgid="4195153527464162486">"64K po međumemoriji zapisnika"</item> - <item msgid="7464037639415220106">"256k po međumemoriji zapisnika"</item> - <item msgid="8539423820514360724">"1M po međumemoriji zapisnika"</item> - <item msgid="1984761927103140651">"4M po međumemoriji zapisnika"</item> - <item msgid="7892098981256010498">"16M po međumemoriji zapisnika"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Isključeno"</item> <item msgid="6014837961827347618">"Sve"</item> diff --git a/packages/SettingsLib/res/values-ca/arrays.xml b/packages/SettingsLib/res/values-ca/arrays.xml index 4b24637ddeaa..c9f63ab9979a 100644 --- a/packages/SettingsLib/res/values-ca/arrays.xml +++ b/packages/SettingsLib/res/values-ca/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", actiu (contingut multimèdia)"</item> <item msgid="5001852592115448348">", actiu (telèfon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"No"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"No"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"No"</item> - <item msgid="4195153527464162486">"64 K / memòria intermèdia del registre"</item> - <item msgid="7464037639415220106">"256 K / memòria intermèdia del registre"</item> - <item msgid="8539423820514360724">"1 M / memòria intermèdia reg."</item> - <item msgid="1984761927103140651">"4 M / memòria intermèdia del registre"</item> - <item msgid="7892098981256010498">"16 M / memòria intermèdia del registre"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Desactivat"</item> <item msgid="6014837961827347618">"Tot"</item> diff --git a/packages/SettingsLib/res/values-cs/arrays.xml b/packages/SettingsLib/res/values-cs/arrays.xml index 27dce16b8dcf..556fc1040411 100644 --- a/packages/SettingsLib/res/values-cs/arrays.xml +++ b/packages/SettingsLib/res/values-cs/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktivní (média)"</item> <item msgid="5001852592115448348">", aktivní (telefon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Vypnuto"</item> - <item msgid="7839165897132179888">"64 kB"</item> - <item msgid="2715700596495505626">"256 kB"</item> - <item msgid="7099386891713159947">"1 MB"</item> - <item msgid="6069075827077845520">"4 MB"</item> - <item msgid="8243549501764402572">"16 MB"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Vypnuto"</item> <item msgid="4064786181089783077">"64 kB"</item> <item msgid="3052710745383602630">"256 kB"</item> <item msgid="3691785423374588514">"1 MB"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Vypnuto"</item> - <item msgid="4195153527464162486">"64 kB na vyrovnávací paměť protokolů"</item> - <item msgid="7464037639415220106">"256 kB na vyrovnávací paměť protokolů"</item> - <item msgid="8539423820514360724">"1 MB na vyrovnávací paměť protokolů"</item> - <item msgid="1984761927103140651">"4 MB na vyrovnávací paměť protokolů"</item> - <item msgid="7892098981256010498">"16 MB na vyrovnávací paměť protokolů"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Vypnuto"</item> <item msgid="6014837961827347618">"Vše"</item> diff --git a/packages/SettingsLib/res/values-da/arrays.xml b/packages/SettingsLib/res/values-da/arrays.xml index efe4150a692d..69b8a091fec8 100644 --- a/packages/SettingsLib/res/values-da/arrays.xml +++ b/packages/SettingsLib/res/values-da/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktiv (medier)"</item> <item msgid="5001852592115448348">", aktiv (telefon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Fra"</item> - <item msgid="7839165897132179888">"64 kB"</item> - <item msgid="2715700596495505626">"256 kB"</item> - <item msgid="7099386891713159947">"1 MB"</item> - <item msgid="6069075827077845520">"4 MB"</item> - <item msgid="8243549501764402572">"16 MB"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Fra"</item> <item msgid="4064786181089783077">"64 kB"</item> <item msgid="3052710745383602630">"256 kB"</item> <item msgid="3691785423374588514">"1 MB"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Fra"</item> - <item msgid="4195153527464162486">"64 kB pr. logbuffer"</item> - <item msgid="7464037639415220106">"256 kB pr. logbuffer"</item> - <item msgid="8539423820514360724">"1 MB pr. logbuffer"</item> - <item msgid="1984761927103140651">"4 MB pr. logbuffer"</item> - <item msgid="7892098981256010498">"16 MB pr. logbuffer"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Fra"</item> <item msgid="6014837961827347618">"Alle"</item> diff --git a/packages/SettingsLib/res/values-de/arrays.xml b/packages/SettingsLib/res/values-de/arrays.xml index e7c488782f29..f6e3496285b7 100644 --- a/packages/SettingsLib/res/values-de/arrays.xml +++ b/packages/SettingsLib/res/values-de/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktiv (Medien)"</item> <item msgid="5001852592115448348">", aktiv (Telefon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Aus"</item> - <item msgid="7839165897132179888">"64.000"</item> - <item msgid="2715700596495505626">"256.000"</item> - <item msgid="7099386891713159947">"1 Mio."</item> - <item msgid="6069075827077845520">"4 Mio."</item> - <item msgid="8243549501764402572">"16 Mio."</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Aus"</item> <item msgid="4064786181089783077">"64.000"</item> <item msgid="3052710745383602630">"256.000"</item> <item msgid="3691785423374588514">"1 Mio."</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Aus"</item> - <item msgid="4195153527464162486">"64.000 pro Puffer"</item> - <item msgid="7464037639415220106">"256.000 pro Puffer"</item> - <item msgid="8539423820514360724">"1 Mio. pro Puffer"</item> - <item msgid="1984761927103140651">"4 Mio. pro Puffer"</item> - <item msgid="7892098981256010498">"16 Mio. pro Puffer"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Aus"</item> <item msgid="6014837961827347618">"Alle"</item> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 16af0322c7c4..ba760dd54020 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -576,8 +576,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string> <string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string> <string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string> - <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) --> - <skip /> + <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobile Daten deaktiviert"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Nicht für Datennutzung konfiguriert"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Kein Telefon"</string> diff --git a/packages/SettingsLib/res/values-el/arrays.xml b/packages/SettingsLib/res/values-el/arrays.xml index 838ca7922a03..2fc7d0b9db25 100644 --- a/packages/SettingsLib/res/values-el/arrays.xml +++ b/packages/SettingsLib/res/values-el/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", ενεργή (μέσα)"</item> <item msgid="5001852592115448348">", ενεργή (τηλέφωνο)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Ανενεργό"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Ανενεργό"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Ανενεργό"</item> - <item msgid="4195153527464162486">"64 K ανά πρ. μν. αρχ. καταγρ."</item> - <item msgid="7464037639415220106">"256 K ανά πρ. μν. αρχ. καταγρ."</item> - <item msgid="8539423820514360724">"1 Μ ανά προσ. μν. αρχ. καταγρ."</item> - <item msgid="1984761927103140651">"4 M ανά προσ. μν. αρχ. καταγρ."</item> - <item msgid="7892098981256010498">"16 M ανά πρ. μν. αρχ. καταγρ."</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Ανενεργό"</item> <item msgid="6014837961827347618">"Όλα"</item> diff --git a/packages/SettingsLib/res/values-en-rAU/arrays.xml b/packages/SettingsLib/res/values-en-rAU/arrays.xml index 6569f18acea0..31dc7c91e6c9 100644 --- a/packages/SettingsLib/res/values-en-rAU/arrays.xml +++ b/packages/SettingsLib/res/values-en-rAU/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", active (media)"</item> <item msgid="5001852592115448348">", active (phone)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Off"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Off"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Off"</item> - <item msgid="4195153527464162486">"64 K per log buffer"</item> - <item msgid="7464037639415220106">"256 K per log buffer"</item> - <item msgid="8539423820514360724">"1 M per log buffer"</item> - <item msgid="1984761927103140651">"4 M per log buffer"</item> - <item msgid="7892098981256010498">"16 M per log buffer"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Off"</item> <item msgid="6014837961827347618">"All"</item> diff --git a/packages/SettingsLib/res/values-en-rCA/arrays.xml b/packages/SettingsLib/res/values-en-rCA/arrays.xml index 6569f18acea0..31dc7c91e6c9 100644 --- a/packages/SettingsLib/res/values-en-rCA/arrays.xml +++ b/packages/SettingsLib/res/values-en-rCA/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", active (media)"</item> <item msgid="5001852592115448348">", active (phone)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Off"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Off"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Off"</item> - <item msgid="4195153527464162486">"64 K per log buffer"</item> - <item msgid="7464037639415220106">"256 K per log buffer"</item> - <item msgid="8539423820514360724">"1 M per log buffer"</item> - <item msgid="1984761927103140651">"4 M per log buffer"</item> - <item msgid="7892098981256010498">"16 M per log buffer"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Off"</item> <item msgid="6014837961827347618">"All"</item> diff --git a/packages/SettingsLib/res/values-en-rGB/arrays.xml b/packages/SettingsLib/res/values-en-rGB/arrays.xml index 6569f18acea0..31dc7c91e6c9 100644 --- a/packages/SettingsLib/res/values-en-rGB/arrays.xml +++ b/packages/SettingsLib/res/values-en-rGB/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", active (media)"</item> <item msgid="5001852592115448348">", active (phone)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Off"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Off"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Off"</item> - <item msgid="4195153527464162486">"64 K per log buffer"</item> - <item msgid="7464037639415220106">"256 K per log buffer"</item> - <item msgid="8539423820514360724">"1 M per log buffer"</item> - <item msgid="1984761927103140651">"4 M per log buffer"</item> - <item msgid="7892098981256010498">"16 M per log buffer"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Off"</item> <item msgid="6014837961827347618">"All"</item> diff --git a/packages/SettingsLib/res/values-en-rIN/arrays.xml b/packages/SettingsLib/res/values-en-rIN/arrays.xml index 6569f18acea0..31dc7c91e6c9 100644 --- a/packages/SettingsLib/res/values-en-rIN/arrays.xml +++ b/packages/SettingsLib/res/values-en-rIN/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", active (media)"</item> <item msgid="5001852592115448348">", active (phone)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Off"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Off"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Off"</item> - <item msgid="4195153527464162486">"64 K per log buffer"</item> - <item msgid="7464037639415220106">"256 K per log buffer"</item> - <item msgid="8539423820514360724">"1 M per log buffer"</item> - <item msgid="1984761927103140651">"4 M per log buffer"</item> - <item msgid="7892098981256010498">"16 M per log buffer"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Off"</item> <item msgid="6014837961827347618">"All"</item> diff --git a/packages/SettingsLib/res/values-en-rXC/arrays.xml b/packages/SettingsLib/res/values-en-rXC/arrays.xml index cb702fea7efa..e4322b9083c3 100644 --- a/packages/SettingsLib/res/values-en-rXC/arrays.xml +++ b/packages/SettingsLib/res/values-en-rXC/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", active (media)"</item> <item msgid="5001852592115448348">", active (phone)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Off"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Off"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Off"</item> - <item msgid="4195153527464162486">"64K per log buffer"</item> - <item msgid="7464037639415220106">"256K per log buffer"</item> - <item msgid="8539423820514360724">"1M per log buffer"</item> - <item msgid="1984761927103140651">"4M per log buffer"</item> - <item msgid="7892098981256010498">"16M per log buffer"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Off"</item> <item msgid="6014837961827347618">"All"</item> diff --git a/packages/SettingsLib/res/values-es-rUS/arrays.xml b/packages/SettingsLib/res/values-es-rUS/arrays.xml index cfce9b66df92..cf6ce2b3ba14 100644 --- a/packages/SettingsLib/res/values-es-rUS/arrays.xml +++ b/packages/SettingsLib/res/values-es-rUS/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", activo (contenido multimedia)"</item> <item msgid="5001852592115448348">", activo (teléfono)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Desactivado"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Desactivado"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Desactivado"</item> - <item msgid="4195153527464162486">"64 K/búfer registro"</item> - <item msgid="7464037639415220106">"256 K/búfer registro"</item> - <item msgid="8539423820514360724">"1 M/búfer registro"</item> - <item msgid="1984761927103140651">"4 M/búfer registro"</item> - <item msgid="7892098981256010498">"16 M/búfer registro"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Desactivado"</item> <item msgid="6014837961827347618">"Todo"</item> diff --git a/packages/SettingsLib/res/values-es/arrays.xml b/packages/SettingsLib/res/values-es/arrays.xml index 5682dd50f952..37f91b2241da 100644 --- a/packages/SettingsLib/res/values-es/arrays.xml +++ b/packages/SettingsLib/res/values-es/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", activo (contenido multimedia)"</item> <item msgid="5001852592115448348">", activo (teléfono)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Desactivado"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Desactivado"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Desactivado"</item> - <item msgid="4195153527464162486">"64 K/búfer registro"</item> - <item msgid="7464037639415220106">"256 K/búfer registro"</item> - <item msgid="8539423820514360724">"1 M/búfer registro"</item> - <item msgid="1984761927103140651">"4 M/búfer registro"</item> - <item msgid="7892098981256010498">"16 M/búfer registro"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Desactivado"</item> <item msgid="6014837961827347618">"Todo"</item> diff --git a/packages/SettingsLib/res/values-et/arrays.xml b/packages/SettingsLib/res/values-et/arrays.xml index 9015cfed4db8..537ea7a49a63 100644 --- a/packages/SettingsLib/res/values-et/arrays.xml +++ b/packages/SettingsLib/res/values-et/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktiivne (meedia)"</item> <item msgid="5001852592115448348">", aktiivne (telefon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Väljas"</item> - <item msgid="7839165897132179888">"64 000"</item> - <item msgid="2715700596495505626">"256 000"</item> - <item msgid="7099386891713159947">"1 000 000"</item> - <item msgid="6069075827077845520">"4 000 000"</item> - <item msgid="8243549501764402572">"16 000 000"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Väljas"</item> <item msgid="4064786181089783077">"64 000"</item> <item msgid="3052710745383602630">"256 000"</item> <item msgid="3691785423374588514">"1 000 000"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Väljas"</item> - <item msgid="4195153527464162486">"64 000 / logipuhver"</item> - <item msgid="7464037639415220106">"256 000 / logipuhver"</item> - <item msgid="8539423820514360724">"1 000 000 / logipuhver"</item> - <item msgid="1984761927103140651">"4 000 000 / logipuhver"</item> - <item msgid="7892098981256010498">"16 000 000 / logipuhver"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Väljas"</item> <item msgid="6014837961827347618">"Kõik"</item> diff --git a/packages/SettingsLib/res/values-eu/arrays.xml b/packages/SettingsLib/res/values-eu/arrays.xml index 0e94bba8e320..367d31c217a3 100644 --- a/packages/SettingsLib/res/values-eu/arrays.xml +++ b/packages/SettingsLib/res/values-eu/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktibo (multimedia-edukia)"</item> <item msgid="5001852592115448348">", aktibo (telefonoa)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Desaktibatuta"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Desaktibatuta"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Desaktibatuta"</item> - <item msgid="4195153527464162486">"64 K erregistroen bufferreko"</item> - <item msgid="7464037639415220106">"256 K erregistroen bufferreko"</item> - <item msgid="8539423820514360724">"1 M erregistroen bufferreko"</item> - <item msgid="1984761927103140651">"4 M erregistroen bufferreko"</item> - <item msgid="7892098981256010498">"16 M erregistroen bufferreko"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Desaktibatuta"</item> <item msgid="6014837961827347618">"Guztiak"</item> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index c7b376a4bd3d..2582854924fe 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -576,8 +576,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string> <string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string> <string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string> - <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) --> - <skip /> + <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Desaktibatuta dago datu-konexioa"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Ez dago ezarrita datuak erabiltzeko"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Ez dago telefono-zenbakirik."</string> diff --git a/packages/SettingsLib/res/values-fa/arrays.xml b/packages/SettingsLib/res/values-fa/arrays.xml index 075f7e09ce5a..070c8ec7ed51 100644 --- a/packages/SettingsLib/res/values-fa/arrays.xml +++ b/packages/SettingsLib/res/values-fa/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">"، فعال (رسانه)"</item> <item msgid="5001852592115448348">"، فعال (تلفن)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"خاموش"</item> - <item msgid="7839165897132179888">"۶۴ هزار"</item> - <item msgid="2715700596495505626">"۲۵۶ هزار"</item> - <item msgid="7099386891713159947">"۱ میلیون"</item> - <item msgid="6069075827077845520">"۴ میلیون"</item> - <item msgid="8243549501764402572">"۱۶ میلیون"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"خاموش"</item> <item msgid="4064786181089783077">"۶۴ هزار"</item> <item msgid="3052710745383602630">"۲۵۶ هزار"</item> <item msgid="3691785423374588514">"۱ میلیون"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"خاموش"</item> - <item msgid="4195153527464162486">"۶۴ هزار در هر بافر گزارش"</item> - <item msgid="7464037639415220106">"۲۵۶ هزار در هر بافر گزارش"</item> - <item msgid="8539423820514360724">"۱ میلیون در هر بافر گزارش"</item> - <item msgid="1984761927103140651">"۴ میلیون در هر بافر گزارش"</item> - <item msgid="7892098981256010498">"۱۶ میلیون در هر بافر گزارش"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"خاموش"</item> <item msgid="6014837961827347618">"همه"</item> diff --git a/packages/SettingsLib/res/values-fi/arrays.xml b/packages/SettingsLib/res/values-fi/arrays.xml index d233c5640a3a..6c38cdf665a8 100644 --- a/packages/SettingsLib/res/values-fi/arrays.xml +++ b/packages/SettingsLib/res/values-fi/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktiivinen (media)"</item> <item msgid="5001852592115448348">", aktiivinen (puhelin)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Ei päällä"</item> - <item msgid="7839165897132179888">"64 kt"</item> - <item msgid="2715700596495505626">"256 kt"</item> - <item msgid="7099386891713159947">"1 Mt"</item> - <item msgid="6069075827077845520">"4 Mt"</item> - <item msgid="8243549501764402572">"16 Mt"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Ei päällä"</item> <item msgid="4064786181089783077">"64 kt"</item> <item msgid="3052710745383602630">"256 kt"</item> <item msgid="3691785423374588514">"1 Mt"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Ei päällä"</item> - <item msgid="4195153527464162486">"64 kt / lokipuskuri"</item> - <item msgid="7464037639415220106">"256 kt / lokipuskuri"</item> - <item msgid="8539423820514360724">"1 Mt / lokipuskuri"</item> - <item msgid="1984761927103140651">"4 Mt / lokipuskuri"</item> - <item msgid="7892098981256010498">"16 Mt / lokipuskuri"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Ei päällä"</item> <item msgid="6014837961827347618">"Kaikki"</item> diff --git a/packages/SettingsLib/res/values-fr-rCA/arrays.xml b/packages/SettingsLib/res/values-fr-rCA/arrays.xml index 1db6540c164e..8d480479f3ee 100644 --- a/packages/SettingsLib/res/values-fr-rCA/arrays.xml +++ b/packages/SettingsLib/res/values-fr-rCA/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", actif (média)"</item> <item msgid="5001852592115448348">", actif (téléphone)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Désactivé"</item> - <item msgid="7839165897132179888">"64 ko"</item> - <item msgid="2715700596495505626">"256 ko"</item> - <item msgid="7099386891713159947">"1 Mo"</item> - <item msgid="6069075827077845520">"4 Mo"</item> - <item msgid="8243549501764402572">"16 Mo"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Désactivé"</item> <item msgid="4064786181089783077">"64 ko"</item> <item msgid="3052710745383602630">"256 ko"</item> <item msgid="3691785423374588514">"1 Mo"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Désactivé"</item> - <item msgid="4195153527464162486">"64 ko/tampon journal"</item> - <item msgid="7464037639415220106">"256 Ko/tampon journal"</item> - <item msgid="8539423820514360724">"1 Mo/tampon journal"</item> - <item msgid="1984761927103140651">"4 Mo/tampon journal"</item> - <item msgid="7892098981256010498">"16 Mo/tampon journal"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Désactivé"</item> <item msgid="6014837961827347618">"Tous"</item> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 58ea7957b68f..e24c1301c804 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -226,7 +226,7 @@ <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Code d\'association Wi-Fi"</string> <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Échec de l\'association"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Vérifier que l\'appareil est connecté au même réseau."</string> - <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Associer un appareil par Wi-Fi en numérisant un code QR"</string> + <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Associez l\'appareil par Wi-Fi en numérisant un code QR"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Association de l\'appareil en cours…"</string> <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Échec de l\'association de l\'appareil Soit le code QR est incorrect, soit l\'appareil n\'est pas connecté au même réseau."</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"Adresse IP et port"</string> @@ -576,8 +576,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string> <string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string> <string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string> - <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) --> - <skip /> + <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Désactivées"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Non configuré pour l\'utilisation des données cellulaires"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Aucun signal"</string> diff --git a/packages/SettingsLib/res/values-fr/arrays.xml b/packages/SettingsLib/res/values-fr/arrays.xml index 7660925f361f..d65ba69229ec 100644 --- a/packages/SettingsLib/res/values-fr/arrays.xml +++ b/packages/SettingsLib/res/values-fr/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", actif (son des médias)"</item> <item msgid="5001852592115448348">", actif (téléphone)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Désactivé"</item> - <item msgid="7839165897132179888">"64 Ko"</item> - <item msgid="2715700596495505626">"256 Ko"</item> - <item msgid="7099386891713159947">"1 Mo"</item> - <item msgid="6069075827077845520">"4 Mo"</item> - <item msgid="8243549501764402572">"16 Mo"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Désactivé"</item> <item msgid="4064786181089783077">"64 Ko"</item> <item msgid="3052710745383602630">"256 Ko"</item> <item msgid="3691785423374588514">"1 Mo"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Désactivé"</item> - <item msgid="4195153527464162486">"64 Ko par tampon journal"</item> - <item msgid="7464037639415220106">"256 Ko par tampon journal"</item> - <item msgid="8539423820514360724">"1 Mo par tampon journal"</item> - <item msgid="1984761927103140651">"4 Mo par tampon journal"</item> - <item msgid="7892098981256010498">"16 Mo par tampon journal"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Désactivé"</item> <item msgid="6014837961827347618">"Tous"</item> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index f321ea32da77..a79ed0c51ef7 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -576,8 +576,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string> <string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string> <string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string> - <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) --> - <skip /> + <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Désactivées"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Non configuré pour utiliser les données"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Aucun signal"</string> diff --git a/packages/SettingsLib/res/values-gl/arrays.xml b/packages/SettingsLib/res/values-gl/arrays.xml index 98f2072d8ba1..f13eaaec390a 100644 --- a/packages/SettingsLib/res/values-gl/arrays.xml +++ b/packages/SettingsLib/res/values-gl/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", activo (contido multimedia)"</item> <item msgid="5001852592115448348">", activo (teléfono)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Desactivado"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Desactivado"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Desactivado"</item> - <item msgid="4195153527464162486">"64 K por búfer de rexistro"</item> - <item msgid="7464037639415220106">"256 K por búfer de rexistro"</item> - <item msgid="8539423820514360724">"1 M por búfer de rexistro"</item> - <item msgid="1984761927103140651">"4 M por búfer de rexistro"</item> - <item msgid="7892098981256010498">"16 M por búfer de rexistro"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Desactivado"</item> <item msgid="6014837961827347618">"Todo"</item> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 254958feddea..97662a65fafe 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -576,8 +576,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string> <string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string> <string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string> - <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) --> - <skip /> + <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"Operador de wifi"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Os datos móbiles están desactivados"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Non se configurou para utilizar datos"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Sen teléfono"</string> diff --git a/packages/SettingsLib/res/values-gu/arrays.xml b/packages/SettingsLib/res/values-gu/arrays.xml index 1a3bf9859e7c..0bbd4f68bdb3 100644 --- a/packages/SettingsLib/res/values-gu/arrays.xml +++ b/packages/SettingsLib/res/values-gu/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", સક્રિય (મીડિયા)"</item> <item msgid="5001852592115448348">", સક્રિય (ફોન)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"બંધ"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"બંધ"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"બંધ"</item> - <item msgid="4195153527464162486">"લૉગ બફર દીઠ 64K"</item> - <item msgid="7464037639415220106">"લૉગ બફર દીઠ 256K"</item> - <item msgid="8539423820514360724">"લૉગ બફર દીઠ 1M"</item> - <item msgid="1984761927103140651">"લૉગ બફર દીઠ 4M"</item> - <item msgid="7892098981256010498">"લૉગ બફર દીઠ 16M"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"બંધ"</item> <item msgid="6014837961827347618">"તમામ"</item> diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml index 36b16e628bc5..ace8b4a1c0b3 100644 --- a/packages/SettingsLib/res/values-hi/arrays.xml +++ b/packages/SettingsLib/res/values-hi/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", चालू है (सिर्फ़ मीडिया के लिए)"</item> <item msgid="5001852592115448348">", चालू है (सिर्फ़ फ़ोन के लिए)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"बंद"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"बंद"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"बंद"</item> - <item msgid="4195153527464162486">"64K प्रति लॉग बफ़र"</item> - <item msgid="7464037639415220106">"256K प्रति लॉग बफ़र"</item> - <item msgid="8539423820514360724">"1 एमबी प्रति लॉग बफ़र"</item> - <item msgid="1984761927103140651">"4M प्रति लॉग बफ़र"</item> - <item msgid="7892098981256010498">"16M प्रति लॉग बफ़र"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"बंद"</item> <item msgid="6014837961827347618">"सभी"</item> diff --git a/packages/SettingsLib/res/values-hr/arrays.xml b/packages/SettingsLib/res/values-hr/arrays.xml index 82a1e4a1ab99..c4188bf88d0c 100644 --- a/packages/SettingsLib/res/values-hr/arrays.xml +++ b/packages/SettingsLib/res/values-hr/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktivno (mediji)"</item> <item msgid="5001852592115448348">", aktivno (telefon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Isključeno"</item> - <item msgid="7839165897132179888">"64 KB"</item> - <item msgid="2715700596495505626">"256 KB"</item> - <item msgid="7099386891713159947">"1 MB"</item> - <item msgid="6069075827077845520">"4 MB"</item> - <item msgid="8243549501764402572">"16 MB"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Isključeno"</item> <item msgid="4064786181089783077">"64 KB"</item> <item msgid="3052710745383602630">"256 KB"</item> <item msgid="3691785423374588514">"1 MB"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Isključeno"</item> - <item msgid="4195153527464162486">"64 KB po međusprem. zapisnika"</item> - <item msgid="7464037639415220106">"256 KB po međusprem. zapisnika"</item> - <item msgid="8539423820514360724">"1 MB po međusprem. zapisnika"</item> - <item msgid="1984761927103140651">"4 MB po međusprem. zapisnika"</item> - <item msgid="7892098981256010498">"16 MB po međusprem. zapisnika"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Isključeno"</item> <item msgid="6014837961827347618">"Sve"</item> diff --git a/packages/SettingsLib/res/values-hu/arrays.xml b/packages/SettingsLib/res/values-hu/arrays.xml index d043af01a23f..cc36c1864246 100644 --- a/packages/SettingsLib/res/values-hu/arrays.xml +++ b/packages/SettingsLib/res/values-hu/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktív (média)"</item> <item msgid="5001852592115448348">", aktív (telefon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Ki"</item> - <item msgid="7839165897132179888">"64 KB"</item> - <item msgid="2715700596495505626">"256 KB"</item> - <item msgid="7099386891713159947">"1 MB"</item> - <item msgid="6069075827077845520">"4 MB"</item> - <item msgid="8243549501764402572">"16 MB"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Ki"</item> <item msgid="4064786181089783077">"64 KB"</item> <item msgid="3052710745383602630">"256 KB"</item> <item msgid="3691785423374588514">"1 MB"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Ki"</item> - <item msgid="4195153527464162486">"64 KB/naplópuffer"</item> - <item msgid="7464037639415220106">"256 KB/naplópuffer"</item> - <item msgid="8539423820514360724">"1 MB/naplópuffer"</item> - <item msgid="1984761927103140651">"4 MB/naplópuffer"</item> - <item msgid="7892098981256010498">"16 MB/naplópuffer"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Kikapcsolva"</item> <item msgid="6014837961827347618">"Összes"</item> diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml index a279872a088e..76ed9bd4a01e 100644 --- a/packages/SettingsLib/res/values-hy/arrays.xml +++ b/packages/SettingsLib/res/values-hy/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", ակտիվ է (մեդիա)"</item> <item msgid="5001852592115448348">", ակտիվ է (հեռախոս)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Անջատված է"</item> - <item msgid="7839165897132179888">"64ԿԲ"</item> - <item msgid="2715700596495505626">"256ԿԲ"</item> - <item msgid="7099386891713159947">"1ՄԲ"</item> - <item msgid="6069075827077845520">"4ՄԲ"</item> - <item msgid="8243549501764402572">"16ՄԲ"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Անջատված է"</item> <item msgid="4064786181089783077">"64ԿԲ"</item> <item msgid="3052710745383602630">"256ԿԲ"</item> <item msgid="3691785423374588514">"1ՄԲ"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Անջատված է"</item> - <item msgid="4195153527464162486">"Բուֆեր՝ առավ. 64ԿԲ"</item> - <item msgid="7464037639415220106">"Բուֆեր՝ առավ. 256ԿԲ"</item> - <item msgid="8539423820514360724">"Բուֆեր՝ առավ. 1ՄԲ"</item> - <item msgid="1984761927103140651">"Բուֆեր՝ առավ. 4ՄԲ"</item> - <item msgid="7892098981256010498">"Բուֆեր՝ առավ. 16ՄԲ"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Անջատված է"</item> <item msgid="6014837961827347618">"Բոլորը"</item> diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml index 3ab50cc948eb..14e331335844 100644 --- a/packages/SettingsLib/res/values-in/arrays.xml +++ b/packages/SettingsLib/res/values-in/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktif (media)"</item> <item msgid="5001852592115448348">", aktif (ponsel)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Nonaktif"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Nonaktif"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Nonaktif"</item> - <item msgid="4195153527464162486">"64 K/buffer log"</item> - <item msgid="7464037639415220106">"256 K/buffer log"</item> - <item msgid="8539423820514360724">"1 M/buffer log"</item> - <item msgid="1984761927103140651">"4 M/buffer log"</item> - <item msgid="7892098981256010498">"16 M/buffer log"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Nonaktif"</item> <item msgid="6014837961827347618">"Semua"</item> diff --git a/packages/SettingsLib/res/values-is/arrays.xml b/packages/SettingsLib/res/values-is/arrays.xml index 93274e4c79c4..7c1773bde209 100644 --- a/packages/SettingsLib/res/values-is/arrays.xml +++ b/packages/SettingsLib/res/values-is/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", virkt (hljóð- og myndefni)"</item> <item msgid="5001852592115448348">", virkt (sími)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Slökkt"</item> - <item msgid="7839165897132179888">"64 k"</item> - <item msgid="2715700596495505626">"256 k"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Slökkt"</item> <item msgid="4064786181089783077">"64 k"</item> <item msgid="3052710745383602630">"256 k"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Slökkt"</item> - <item msgid="4195153527464162486">"64 k/biðminni"</item> - <item msgid="7464037639415220106">"256 k/biðminni"</item> - <item msgid="8539423820514360724">"1 M/biðminni"</item> - <item msgid="1984761927103140651">"4 M/biðminni"</item> - <item msgid="7892098981256010498">"16 M/biðminni"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Slökkt"</item> <item msgid="6014837961827347618">"Allt"</item> diff --git a/packages/SettingsLib/res/values-it/arrays.xml b/packages/SettingsLib/res/values-it/arrays.xml index 57c0c9b29ec7..127903fd9a20 100644 --- a/packages/SettingsLib/res/values-it/arrays.xml +++ b/packages/SettingsLib/res/values-it/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", attivo (contenuti multimediali)"</item> <item msgid="5001852592115448348">", attivo (telefono)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Off"</item> - <item msgid="7839165897132179888">"64 kB"</item> - <item msgid="2715700596495505626">"256 kB"</item> - <item msgid="7099386891713159947">"1 MB"</item> - <item msgid="6069075827077845520">"4 MB"</item> - <item msgid="8243549501764402572">"16 MB"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Off"</item> <item msgid="4064786181089783077">"64 kB"</item> <item msgid="3052710745383602630">"256 kB"</item> <item msgid="3691785423374588514">"1 MB"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Off"</item> - <item msgid="4195153527464162486">"64 kB/buffer log"</item> - <item msgid="7464037639415220106">"256 kB/buffer log"</item> - <item msgid="8539423820514360724">"1 MB/buffer log"</item> - <item msgid="1984761927103140651">"4 MB/buffer log"</item> - <item msgid="7892098981256010498">"16 MB/buffer log"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Off"</item> <item msgid="6014837961827347618">"Tutti"</item> diff --git a/packages/SettingsLib/res/values-iw/arrays.xml b/packages/SettingsLib/res/values-iw/arrays.xml index fa53ab8ed45d..151b825b2d26 100644 --- a/packages/SettingsLib/res/values-iw/arrays.xml +++ b/packages/SettingsLib/res/values-iw/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", פעיל (מדיה)"</item> <item msgid="5001852592115448348">", פעיל (טלפון)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"כבוי"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"כבוי"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"כבוי"</item> - <item msgid="4195153527464162486">"64K לכל מאגר של יומן רישום"</item> - <item msgid="7464037639415220106">"256K לכל מאגר של יומן רישום"</item> - <item msgid="8539423820514360724">"1M לכל מאגר של יומן רישום"</item> - <item msgid="1984761927103140651">"4M לכל מאגר של יומן רישום"</item> - <item msgid="7892098981256010498">"16M לכל מאגר של יומן רישום"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"כבוי"</item> <item msgid="6014837961827347618">"הכול"</item> diff --git a/packages/SettingsLib/res/values-ja/arrays.xml b/packages/SettingsLib/res/values-ja/arrays.xml index 7a4e71b18ec0..1401069ebef4 100644 --- a/packages/SettingsLib/res/values-ja/arrays.xml +++ b/packages/SettingsLib/res/values-ja/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">"、有効(メディア)"</item> <item msgid="5001852592115448348">"、有効(スマートフォン)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"OFF"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"OFF"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"OFF"</item> - <item msgid="4195153527464162486">"64 K / ログバッファ"</item> - <item msgid="7464037639415220106">"256 K / ログバッファ"</item> - <item msgid="8539423820514360724">"1 M / ログバッファ"</item> - <item msgid="1984761927103140651">"4 M / ログバッファ"</item> - <item msgid="7892098981256010498">"16 M / ログバッファ"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"OFF"</item> <item msgid="6014837961827347618">"すべて"</item> diff --git a/packages/SettingsLib/res/values-ka/arrays.xml b/packages/SettingsLib/res/values-ka/arrays.xml index 935cc461c74d..62ae1e626bd1 100644 --- a/packages/SettingsLib/res/values-ka/arrays.xml +++ b/packages/SettingsLib/res/values-ka/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", აქტიური (მედია)"</item> <item msgid="5001852592115448348">", აქტიური (ტელეფონი)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"გამორთული"</item> - <item msgid="7839165897132179888">"64 კბაიტი"</item> - <item msgid="2715700596495505626">"256 კბაიტი"</item> - <item msgid="7099386891713159947">"1 მბაიტი"</item> - <item msgid="6069075827077845520">"4 მბაიტი"</item> - <item msgid="8243549501764402572">"16 მბაიტი"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"გამორთული"</item> <item msgid="4064786181089783077">"64 კბაიტი"</item> <item msgid="3052710745383602630">"256 კბაიტი"</item> <item msgid="3691785423374588514">"1 მბაიტი"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"გამორთული"</item> - <item msgid="4195153527464162486">"64 კბაიტი / ჟურნალის ბუფერი"</item> - <item msgid="7464037639415220106">"256 კბაიტი / ჟურნალის ბუფერი"</item> - <item msgid="8539423820514360724">"1 მბაიტი / ჟურნალის ბუფერი"</item> - <item msgid="1984761927103140651">"4 მბაიტი / ჟურნალის ბუფერი"</item> - <item msgid="7892098981256010498">"16 მბაიტი / ჟურნალის ბუფერი"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"გამორთული"</item> <item msgid="6014837961827347618">"ყველა"</item> diff --git a/packages/SettingsLib/res/values-kk/arrays.xml b/packages/SettingsLib/res/values-kk/arrays.xml index faa8af821a92..a2fe014e33da 100644 --- a/packages/SettingsLib/res/values-kk/arrays.xml +++ b/packages/SettingsLib/res/values-kk/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", қосулы (медиамазмұн)"</item> <item msgid="5001852592115448348">", қосулы (телефон)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Өшірулі"</item> - <item msgid="7839165897132179888">"64 КБ"</item> - <item msgid="2715700596495505626">"256 КБ"</item> - <item msgid="7099386891713159947">"1 МБ"</item> - <item msgid="6069075827077845520">"4 МБ"</item> - <item msgid="8243549501764402572">"16 МБ"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Өшірулі"</item> <item msgid="4064786181089783077">"64 КБ"</item> <item msgid="3052710745383602630">"256 КБ"</item> <item msgid="3691785423374588514">"1 МБ"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Өшірулі"</item> - <item msgid="4195153527464162486">"Әр журнал буферіне 64 КБ"</item> - <item msgid="7464037639415220106">"Әр журнал буферіне 256 КБ"</item> - <item msgid="8539423820514360724">"Әр журнал буферіне 1 МБ"</item> - <item msgid="1984761927103140651">"Әр журнал буферіне 4 МБ"</item> - <item msgid="7892098981256010498">"Әр журнал буферіне 16 МБ"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Өшірулі"</item> <item msgid="6014837961827347618">"Барлығы"</item> diff --git a/packages/SettingsLib/res/values-km/arrays.xml b/packages/SettingsLib/res/values-km/arrays.xml index 6f4589e288e6..70c1e33235b2 100644 --- a/packages/SettingsLib/res/values-km/arrays.xml +++ b/packages/SettingsLib/res/values-km/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">"សកម្ម (មេឌៀ)"</item> <item msgid="5001852592115448348">"សកម្ម (ទូរសព្ទ)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"បិទ"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"បិទ"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"បិទ"</item> - <item msgid="4195153527464162486">"64K per log buffer"</item> - <item msgid="7464037639415220106">"256K per log buffer"</item> - <item msgid="8539423820514360724">"1M per log buffer"</item> - <item msgid="1984761927103140651">"4M per log buffer"</item> - <item msgid="7892098981256010498">"16M per log buffer"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"បិទ"</item> <item msgid="6014837961827347618">"ទាំងអស់"</item> diff --git a/packages/SettingsLib/res/values-kn/arrays.xml b/packages/SettingsLib/res/values-kn/arrays.xml index 7e543dd7c48c..1bfcdc0fbbde 100644 --- a/packages/SettingsLib/res/values-kn/arrays.xml +++ b/packages/SettingsLib/res/values-kn/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", ಸಕ್ರಿಯ (ಮಾಧ್ಯಮ)"</item> <item msgid="5001852592115448348">", ಸಕ್ರಿಯ (ಫೋನ್)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"ಆಫ್"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"ಆಫ್"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"ಆಫ್"</item> - <item msgid="4195153527464162486">"ಪ್ರತಿ ಲಾಗ್ ಬಫರ್ಗೆ 64K"</item> - <item msgid="7464037639415220106">"ಪ್ರತಿ ಲಾಗ್ ಬಫರ್ಗೆ 256K"</item> - <item msgid="8539423820514360724">"ಪ್ರತಿ ಲಾಗ್ ಬಫರ್ಗೆ 1M"</item> - <item msgid="1984761927103140651">"ಪ್ರತಿ ಲಾಗ್ ಬಫರ್ಗೆ 4M"</item> - <item msgid="7892098981256010498">"ಪ್ರತಿ ಲಾಗ್ ಬಫರ್ಗೆ 16M"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"ಆಫ್"</item> <item msgid="6014837961827347618">"ಎಲ್ಲಾ"</item> diff --git a/packages/SettingsLib/res/values-ko/arrays.xml b/packages/SettingsLib/res/values-ko/arrays.xml index 9a18c16b6fd4..648188f29e1f 100644 --- a/packages/SettingsLib/res/values-ko/arrays.xml +++ b/packages/SettingsLib/res/values-ko/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", 활성(미디어)"</item> <item msgid="5001852592115448348">", 활성(휴대전화)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"사용 안함"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"사용 안함"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"사용 안함"</item> - <item msgid="4195153527464162486">"로그 버퍼당 64K"</item> - <item msgid="7464037639415220106">"로그 버퍼당 256K"</item> - <item msgid="8539423820514360724">"로그 버퍼당 1M"</item> - <item msgid="1984761927103140651">"로그 버퍼당 4M"</item> - <item msgid="7892098981256010498">"로그 버퍼당 16M"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"사용 안함"</item> <item msgid="6014837961827347618">"전체"</item> diff --git a/packages/SettingsLib/res/values-ky/arrays.xml b/packages/SettingsLib/res/values-ky/arrays.xml index f5e812dab9c9..295c17461816 100644 --- a/packages/SettingsLib/res/values-ky/arrays.xml +++ b/packages/SettingsLib/res/values-ky/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", жандырылган (аудио)"</item> <item msgid="5001852592115448348">", жандырылган (телефон)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Өчүк"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Өчүк"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Өчүк"</item> - <item msgid="4195153527464162486">"Буфер: 64КБ ашпашы керек"</item> - <item msgid="7464037639415220106">"Буфер: 256КБ ашпашы керек"</item> - <item msgid="8539423820514360724">"Буфер: 1М ашпашы керек"</item> - <item msgid="1984761927103140651">"Буфер: 4М ашпашы керек"</item> - <item msgid="7892098981256010498">"Буфер: 16М ашпашы керек"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Өчүк"</item> <item msgid="6014837961827347618">"Бардыгы"</item> diff --git a/packages/SettingsLib/res/values-lo/arrays.xml b/packages/SettingsLib/res/values-lo/arrays.xml index a0fb2b8966a2..c48eb3b43a70 100644 --- a/packages/SettingsLib/res/values-lo/arrays.xml +++ b/packages/SettingsLib/res/values-lo/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", ອອນລາຍ (ມີເດຍ)"</item> <item msgid="5001852592115448348">", ອອນລາຍ (ໂທລະສັບ)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"ປິດ"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"ປິດ"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"ປິດ"</item> - <item msgid="4195153527464162486">"ບັບເຟີ 64K ຕໍ່ບັນທຶກ"</item> - <item msgid="7464037639415220106">"ບັບເຟີ 256K ຕໍ່ບັນທຶກ"</item> - <item msgid="8539423820514360724">"ບັບເຟີ 1M ຕໍ່ບັນທຶກ"</item> - <item msgid="1984761927103140651">"ບັບເຟີ 4M ຕໍ່ບັນທຶກ"</item> - <item msgid="7892098981256010498">"ບັບເຟີ 16M ຕໍ່ບັນທຶກ"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"ປິດ"</item> <item msgid="6014837961827347618">"ທັງໝົດ"</item> diff --git a/packages/SettingsLib/res/values-lt/arrays.xml b/packages/SettingsLib/res/values-lt/arrays.xml index 90a77bf738cc..48b69c806de9 100644 --- a/packages/SettingsLib/res/values-lt/arrays.xml +++ b/packages/SettingsLib/res/values-lt/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktyvus (medija)"</item> <item msgid="5001852592115448348">", aktyvus (telefonas)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Išjungta"</item> - <item msgid="7839165897132179888">"64 KB"</item> - <item msgid="2715700596495505626">"256 KB"</item> - <item msgid="7099386891713159947">"1 MB"</item> - <item msgid="6069075827077845520">"4 MB"</item> - <item msgid="8243549501764402572">"16 MB"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Išjungta"</item> <item msgid="4064786181089783077">"64 KB"</item> <item msgid="3052710745383602630">"256 KB"</item> <item msgid="3691785423374588514">"1 MB"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Išjungta"</item> - <item msgid="4195153527464162486">"64 KB žurnalo buferis"</item> - <item msgid="7464037639415220106">"256 KB žurnalo buferis"</item> - <item msgid="8539423820514360724">"1 MB žurnalo buferis"</item> - <item msgid="1984761927103140651">"4 MB žurnalo buferis"</item> - <item msgid="7892098981256010498">"16 MB žurnalo buferis"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Išjungta"</item> <item msgid="6014837961827347618">"Viskas"</item> diff --git a/packages/SettingsLib/res/values-lv/arrays.xml b/packages/SettingsLib/res/values-lv/arrays.xml index 589172756a73..81a372160737 100644 --- a/packages/SettingsLib/res/values-lv/arrays.xml +++ b/packages/SettingsLib/res/values-lv/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktīva (multivide)"</item> <item msgid="5001852592115448348">", aktīva (tālrunis)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Izslēgts"</item> - <item msgid="7839165897132179888">"64 KB"</item> - <item msgid="2715700596495505626">"256 KB"</item> - <item msgid="7099386891713159947">"1 MB"</item> - <item msgid="6069075827077845520">"4 MB"</item> - <item msgid="8243549501764402572">"16 MB"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Izslēgts"</item> <item msgid="4064786181089783077">"64 KB"</item> <item msgid="3052710745383602630">"256 KB"</item> <item msgid="3691785423374588514">"1 MB"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Izslēgts"</item> - <item msgid="4195153527464162486">"64 KB vienam žurnāla buferim"</item> - <item msgid="7464037639415220106">"256 KB vienam žurnāla buferim"</item> - <item msgid="8539423820514360724">"1 MB vienam žurnāla buferim"</item> - <item msgid="1984761927103140651">"4 MB vienam žurnāla buferim"</item> - <item msgid="7892098981256010498">"16 MB vienam žurnāla buferim"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Izslēgts"</item> <item msgid="6014837961827347618">"Visi"</item> diff --git a/packages/SettingsLib/res/values-mk/arrays.xml b/packages/SettingsLib/res/values-mk/arrays.xml index 388e280196ec..90a97c74e888 100644 --- a/packages/SettingsLib/res/values-mk/arrays.xml +++ b/packages/SettingsLib/res/values-mk/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", активен (аудиосодржини)"</item> <item msgid="5001852592115448348">", активен (телефон)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Исклучено"</item> - <item msgid="7839165897132179888">"64.000"</item> - <item msgid="2715700596495505626">"256.000"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Исклучено"</item> <item msgid="4064786181089783077">"64.000"</item> <item msgid="3052710745383602630">"256.000"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Исклучено"</item> - <item msgid="4195153527464162486">"64 K/меѓумеморија"</item> - <item msgid="7464037639415220106">"256 K/меѓумеморија"</item> - <item msgid="8539423820514360724">"1 M/меѓумеморија"</item> - <item msgid="1984761927103140651">"4 M/меѓумеморија"</item> - <item msgid="7892098981256010498">"16 M/меѓумеморија"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Исклучено"</item> <item msgid="6014837961827347618">"Сите"</item> diff --git a/packages/SettingsLib/res/values-ml/arrays.xml b/packages/SettingsLib/res/values-ml/arrays.xml index cb31d22f4be0..5ea0615b7c27 100644 --- a/packages/SettingsLib/res/values-ml/arrays.xml +++ b/packages/SettingsLib/res/values-ml/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", സജീവ (മീഡിയ)"</item> <item msgid="5001852592115448348">", സജീവമായ (ഫോൺ)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"ഓഫ്"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"ഓഫ്"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"ഓഫ്"</item> - <item msgid="4195153527464162486">"ഓരോ ലോഗ് ബഫറിനും 64K"</item> - <item msgid="7464037639415220106">"ഓരോ ലോഗ് ബഫറിനും 256K"</item> - <item msgid="8539423820514360724">"ഓരോ ലോഗ് ബഫറിനും 1M"</item> - <item msgid="1984761927103140651">"ഓരോ ലോഗ് ബഫറിനും 4M"</item> - <item msgid="7892098981256010498">"ഓരോ ലോഗ് ബഫറിനും 16M"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"ഓഫ്"</item> <item msgid="6014837961827347618">"എല്ലാം"</item> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 485a228da344..1455669fbc85 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -576,8 +576,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string> <string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string> <string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string> - <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) --> - <skip /> + <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"മൊബൈൽ ഡാറ്റ ഓഫാണ്"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"ഡാറ്റ ഉപയോഗിക്കുന്നതിന് സജ്ജീകരിച്ചിട്ടില്ല"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"ഫോൺ സിഗ്നൽ ഒന്നുമില്ല."</string> diff --git a/packages/SettingsLib/res/values-mn/arrays.xml b/packages/SettingsLib/res/values-mn/arrays.xml index 6a33b48abcaf..e58ff662f0b0 100644 --- a/packages/SettingsLib/res/values-mn/arrays.xml +++ b/packages/SettingsLib/res/values-mn/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", идэвхтэй (медиа)"</item> <item msgid="5001852592115448348">", идэвхтэй (утас)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Идэвхгүй"</item> - <item msgid="7839165897132179888">"64000"</item> - <item msgid="2715700596495505626">"256000"</item> - <item msgid="7099386891713159947">"1 сая"</item> - <item msgid="6069075827077845520">"4 сая"</item> - <item msgid="8243549501764402572">"16 сая"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Идэвхгүй"</item> <item msgid="4064786181089783077">"64000"</item> <item msgid="3052710745383602630">"256000"</item> <item msgid="3691785423374588514">"1 сая"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Идэвхгүй"</item> - <item msgid="4195153527464162486">"лог буфер бүрд 64K"</item> - <item msgid="7464037639415220106">"лог буфер бүрд 256K"</item> - <item msgid="8539423820514360724">"лог буфер бүрд 1M"</item> - <item msgid="1984761927103140651">"лог буфер бүрд 4M"</item> - <item msgid="7892098981256010498">"лог буфер бүрд 16M"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Идэвхгүй"</item> <item msgid="6014837961827347618">"Бүгд"</item> diff --git a/packages/SettingsLib/res/values-mr/arrays.xml b/packages/SettingsLib/res/values-mr/arrays.xml index 8abf290a71cc..aaf51b3d8a68 100644 --- a/packages/SettingsLib/res/values-mr/arrays.xml +++ b/packages/SettingsLib/res/values-mr/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", अॅक्टिव्ह (मीडिया)"</item> <item msgid="5001852592115448348">", अॅक्टिव्ह (फोन)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"बंद"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"बंद"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"बंद"</item> - <item msgid="4195153527464162486">"प्रति लॉग बफर 64K"</item> - <item msgid="7464037639415220106">"प्रति लॉग बफर 256K"</item> - <item msgid="8539423820514360724">"प्रति लॉग बफर 1M"</item> - <item msgid="1984761927103140651">"प्रति लॉग बफर 4M"</item> - <item msgid="7892098981256010498">"प्रति लॉग बफर 16M"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"बंद"</item> <item msgid="6014837961827347618">"सर्व"</item> diff --git a/packages/SettingsLib/res/values-ms/arrays.xml b/packages/SettingsLib/res/values-ms/arrays.xml index 15fad674bf2d..d2fc10ef4996 100644 --- a/packages/SettingsLib/res/values-ms/arrays.xml +++ b/packages/SettingsLib/res/values-ms/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktif (media)"</item> <item msgid="5001852592115448348">", aktif (telefon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Mati"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Mati"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Mati"</item> - <item msgid="4195153527464162486">"64K per penimbal log"</item> - <item msgid="7464037639415220106">"256K per penimbal log"</item> - <item msgid="8539423820514360724">"1M per penimbal log"</item> - <item msgid="1984761927103140651">"4M per penimbal log"</item> - <item msgid="7892098981256010498">"16M per penimbal log"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Mati"</item> <item msgid="6014837961827347618">"Semua"</item> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 1ba076dc6f49..9527793b179e 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -576,8 +576,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string> <string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string> <string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string> - <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) --> - <skip /> + <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Data mudah alih dimatikan"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Tidak ditetapkan untuk menggunakan data"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Tiada telefon."</string> diff --git a/packages/SettingsLib/res/values-my/arrays.xml b/packages/SettingsLib/res/values-my/arrays.xml index 90bac819e3fd..3c693355b374 100644 --- a/packages/SettingsLib/res/values-my/arrays.xml +++ b/packages/SettingsLib/res/values-my/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">"၊ ဖွင့်ထားသည် (မီဒီယာ)"</item> <item msgid="5001852592115448348">"၊ ဖွင့်ထားသည် (ဖုန်း)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"ပိတ်ရန်"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"ပိတ်ရန်"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"ပိတ်ရန်"</item> - <item msgid="4195153527464162486">"မှတ်တမ်းယာယီကြားခံနယ်တစ်ခုလျှင် 64K"</item> - <item msgid="7464037639415220106">"မှတ်တမ်းယာယီကြားခံနယ်တစ်ခုလျှင် 256K"</item> - <item msgid="8539423820514360724">"မှတ်တမ်းကြားခံနယ် တစ်ခုလျှင် 1M"</item> - <item msgid="1984761927103140651">"မှတ်တမ်းယာယီကြားခံနယ်တစ်ခုလျှင် 4M"</item> - <item msgid="7892098981256010498">"မှတ်တမ်းယာယီကြားခံနယ်တစ်ခုလျှင် 16M"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"ပိတ်ရန်"</item> <item msgid="6014837961827347618">"အားလုံး"</item> diff --git a/packages/SettingsLib/res/values-nb/arrays.xml b/packages/SettingsLib/res/values-nb/arrays.xml index 275018b95c23..5e6ee65dbe02 100644 --- a/packages/SettingsLib/res/values-nb/arrays.xml +++ b/packages/SettingsLib/res/values-nb/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktiv (media)"</item> <item msgid="5001852592115448348">", aktiv (telefon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Av"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Av"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Av"</item> - <item msgid="4195153527464162486">"64K per loggbuffer"</item> - <item msgid="7464037639415220106">"256K per loggbuffer"</item> - <item msgid="8539423820514360724">"1M per loggbuffer"</item> - <item msgid="1984761927103140651">"4M per loggbuffer"</item> - <item msgid="7892098981256010498">"16M per loggbuffer"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Av"</item> <item msgid="6014837961827347618">"Alle"</item> diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml index 5ee6353f0962..c8b89c726d60 100644 --- a/packages/SettingsLib/res/values-ne/arrays.xml +++ b/packages/SettingsLib/res/values-ne/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", सक्रिय (मिडिया)"</item> <item msgid="5001852592115448348">", सक्रिय (फोन)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"निष्क्रिय गर्नुहोस्"</item> - <item msgid="7839165897132179888">"६४के"</item> - <item msgid="2715700596495505626">"२५६के"</item> - <item msgid="7099386891713159947">"१एम"</item> - <item msgid="6069075827077845520">"४एम"</item> - <item msgid="8243549501764402572">"१६एम"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"निष्क्रिय गर्नुहोस्"</item> <item msgid="4064786181089783077">"६४के"</item> <item msgid="3052710745383602630">"२५६के"</item> <item msgid="3691785423374588514">"१एम"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"निष्क्रिय गर्नुहोस्"</item> - <item msgid="4195153527464162486">"६४के प्रति लग बफर"</item> - <item msgid="7464037639415220106">"२५६के प्रति लग बफर"</item> - <item msgid="8539423820514360724">"१एम प्रति लग बफर"</item> - <item msgid="1984761927103140651">"४एम प्रति लग बफर"</item> - <item msgid="7892098981256010498">"१६एम प्रति लग बफर"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"निष्क्रिय"</item> <item msgid="6014837961827347618">"सबै"</item> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index c0e9fe56e118..5795cc9b6d92 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -576,8 +576,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string> <string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string> <string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string> - <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) --> - <skip /> + <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"मोबाइल डेटा निष्क्रिय छ"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"डेटा प्रयोग गर्ने गरी सेट गरिएन"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"फोन छैन्।"</string> diff --git a/packages/SettingsLib/res/values-nl/arrays.xml b/packages/SettingsLib/res/values-nl/arrays.xml index bfbbae041a50..a20db9df2737 100644 --- a/packages/SettingsLib/res/values-nl/arrays.xml +++ b/packages/SettingsLib/res/values-nl/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", actief (media)"</item> <item msgid="5001852592115448348">", actief (telefoon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Uit"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Uit"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Uit"</item> - <item msgid="4195153527464162486">"64 K per logbuffer"</item> - <item msgid="7464037639415220106">"256 K per logbuffer"</item> - <item msgid="8539423820514360724">"1 M per logbuffer"</item> - <item msgid="1984761927103140651">"4 M per logbuffer"</item> - <item msgid="7892098981256010498">"16 M per logbuffer"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Uit"</item> <item msgid="6014837961827347618">"Alle"</item> diff --git a/packages/SettingsLib/res/values-or/arrays.xml b/packages/SettingsLib/res/values-or/arrays.xml index 4a3d5d7c325f..b7de25ebff4a 100644 --- a/packages/SettingsLib/res/values-or/arrays.xml +++ b/packages/SettingsLib/res/values-or/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", ସକ୍ରିୟ (ମିଡିଆ)"</item> <item msgid="5001852592115448348">", ସକ୍ରିୟ (ଫୋନ୍)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"ବନ୍ଦ"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"ବନ୍ଦ"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"ବନ୍ଦ"</item> - <item msgid="4195153527464162486">"64K ପିଛା ଲଗ୍ ବଫର୍"</item> - <item msgid="7464037639415220106">"256K ଲଗ୍ ପ୍ରତି ବଫର୍"</item> - <item msgid="8539423820514360724">"1M ପ୍ରତି ଲଗ୍ ବଫର୍"</item> - <item msgid="1984761927103140651">"ଲଗ୍ ବଫର୍ ପ୍ରତି 4M"</item> - <item msgid="7892098981256010498">"16M ଲଗ ପିଛା ବଫର୍"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"ବନ୍ଦ"</item> <item msgid="6014837961827347618">"ସମସ୍ତ"</item> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index ac1ccb612896..41e84f9e1a84 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -576,8 +576,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string> <string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string> <string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string> - <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) --> - <skip /> + <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"ମୋବାଇଲ୍ ଡାଟା ବନ୍ଦ ଅଛି"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"ବ୍ୟବହୃତ ଡାଟା ପାଇଁ ସେଟ୍ ହୋଇନାହିଁ"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"କୌଣସି ଫୋନ୍ ନାହିଁ।"</string> diff --git a/packages/SettingsLib/res/values-pa/arrays.xml b/packages/SettingsLib/res/values-pa/arrays.xml index d594f3b52644..c64ee7604058 100644 --- a/packages/SettingsLib/res/values-pa/arrays.xml +++ b/packages/SettingsLib/res/values-pa/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", ਕਿਰਿਆਸ਼ੀਲ (ਮੀਡੀਆ)"</item> <item msgid="5001852592115448348">", ਕਿਰਿਆਸ਼ੀਲ (ਫ਼ੋਨ)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"ਬੰਦ"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"ਬੰਦ"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"ਬੰਦ"</item> - <item msgid="4195153527464162486">"64K ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item> - <item msgid="7464037639415220106">"256K ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item> - <item msgid="8539423820514360724">"1M ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item> - <item msgid="1984761927103140651">"4M ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item> - <item msgid="7892098981256010498">"16M ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"ਬੰਦ"</item> <item msgid="6014837961827347618">"ਸਭ"</item> diff --git a/packages/SettingsLib/res/values-pl/arrays.xml b/packages/SettingsLib/res/values-pl/arrays.xml index eb33323a06e5..e873b7e5271a 100644 --- a/packages/SettingsLib/res/values-pl/arrays.xml +++ b/packages/SettingsLib/res/values-pl/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktywne (multimedia)"</item> <item msgid="5001852592115448348">", aktywne (telefon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Wył."</item> - <item msgid="7839165897132179888">"64 KB"</item> - <item msgid="2715700596495505626">"256 KB"</item> - <item msgid="7099386891713159947">"1 MB"</item> - <item msgid="6069075827077845520">"4 MB"</item> - <item msgid="8243549501764402572">"16 MB"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Wył."</item> <item msgid="4064786181089783077">"64 KB"</item> <item msgid="3052710745383602630">"256 KB"</item> <item msgid="3691785423374588514">"1 MB"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Wył."</item> - <item msgid="4195153527464162486">"64 KB/bufor dziennika"</item> - <item msgid="7464037639415220106">"256 KB/bufor dziennika"</item> - <item msgid="8539423820514360724">"1 MB/bufor dziennika"</item> - <item msgid="1984761927103140651">"4 MB/bufor dziennika"</item> - <item msgid="7892098981256010498">"16 MB/bufor dziennika"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Wyłączone"</item> <item msgid="6014837961827347618">"Wszystkie"</item> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index cd6f25cbd7b5..a57e5413e9aa 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -578,8 +578,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string> <string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string> <string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string> - <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) --> - <skip /> + <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Wyłączona"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Nie skonfigurowano do transmisji danych"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Brak sygnału telefonu."</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/arrays.xml b/packages/SettingsLib/res/values-pt-rBR/arrays.xml index 81285aa57311..c0dcec028736 100644 --- a/packages/SettingsLib/res/values-pt-rBR/arrays.xml +++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", ativo (mídia)"</item> <item msgid="5001852592115448348">", ativo (telefone)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Desativado"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Desativado"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Desativado"</item> - <item msgid="4195153527464162486">"64 K/buffer de registro"</item> - <item msgid="7464037639415220106">"256 K/buffer de registro"</item> - <item msgid="8539423820514360724">"1 M/buffer de registro"</item> - <item msgid="1984761927103140651">"4 M/buffer de registro"</item> - <item msgid="7892098981256010498">"16 M/buffer de registro"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Desativado"</item> <item msgid="6014837961827347618">"Todos"</item> diff --git a/packages/SettingsLib/res/values-pt-rPT/arrays.xml b/packages/SettingsLib/res/values-pt-rPT/arrays.xml index 019a5f6ed15c..de63257fe1be 100644 --- a/packages/SettingsLib/res/values-pt-rPT/arrays.xml +++ b/packages/SettingsLib/res/values-pt-rPT/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", ativo (multimédia)"</item> <item msgid="5001852592115448348">", ativo (telemóvel)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Desativado"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Desativado"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Desativado"</item> - <item msgid="4195153527464162486">"64 K por buffer de registo"</item> - <item msgid="7464037639415220106">"256 K por buffer de registo"</item> - <item msgid="8539423820514360724">"1 M por buffer de registo"</item> - <item msgid="1984761927103140651">"4 M por buffer de registo"</item> - <item msgid="7892098981256010498">"16 M por buffer de registo"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Desativado"</item> <item msgid="6014837961827347618">"Todos"</item> diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml index 81285aa57311..c0dcec028736 100644 --- a/packages/SettingsLib/res/values-pt/arrays.xml +++ b/packages/SettingsLib/res/values-pt/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", ativo (mídia)"</item> <item msgid="5001852592115448348">", ativo (telefone)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Desativado"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Desativado"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Desativado"</item> - <item msgid="4195153527464162486">"64 K/buffer de registro"</item> - <item msgid="7464037639415220106">"256 K/buffer de registro"</item> - <item msgid="8539423820514360724">"1 M/buffer de registro"</item> - <item msgid="1984761927103140651">"4 M/buffer de registro"</item> - <item msgid="7892098981256010498">"16 M/buffer de registro"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Desativado"</item> <item msgid="6014837961827347618">"Todos"</item> diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml index 48a3fa7db5fd..0fe0ef0da30c 100644 --- a/packages/SettingsLib/res/values-ro/arrays.xml +++ b/packages/SettingsLib/res/values-ro/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", activ (media)"</item> <item msgid="5001852592115448348">", activ (telefon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Dezactivată"</item> - <item msgid="7839165897132179888">"64 KB"</item> - <item msgid="2715700596495505626">"256 KB"</item> - <item msgid="7099386891713159947">"1 MB"</item> - <item msgid="6069075827077845520">"4 MB"</item> - <item msgid="8243549501764402572">"16 MB"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Dezactivată"</item> <item msgid="4064786181089783077">"64 KB"</item> <item msgid="3052710745383602630">"256 KB"</item> <item msgid="3691785423374588514">"1 MB"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Dezactivată"</item> - <item msgid="4195153527464162486">"64 KB/mem. temporară de înregistrări în jurnal"</item> - <item msgid="7464037639415220106">"256 KB/mem. temporară de înregistrări în jurnal"</item> - <item msgid="8539423820514360724">"1 MB/mem. temporară de înregistrări în jurnal"</item> - <item msgid="1984761927103140651">"4 MB/mem. temporară de înregistrări în jurnal"</item> - <item msgid="7892098981256010498">"16 MB/mem. temporară de înregistrări în jurnal"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Dezactivată"</item> <item msgid="6014837961827347618">"Toate"</item> diff --git a/packages/SettingsLib/res/values-ru/arrays.xml b/packages/SettingsLib/res/values-ru/arrays.xml index 5617aa6e1c52..84c3dc6a0f2f 100644 --- a/packages/SettingsLib/res/values-ru/arrays.xml +++ b/packages/SettingsLib/res/values-ru/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", активно (A2DP)"</item> <item msgid="5001852592115448348">", активно (HSP/HFP)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Отключено"</item> - <item msgid="7839165897132179888">"64 КБ"</item> - <item msgid="2715700596495505626">"256 КБ"</item> - <item msgid="7099386891713159947">"1 МБ"</item> - <item msgid="6069075827077845520">"4 МБ"</item> - <item msgid="8243549501764402572">"16 МБ"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Отключено"</item> <item msgid="4064786181089783077">"64 КБ"</item> <item msgid="3052710745383602630">"256 КБ"</item> <item msgid="3691785423374588514">"1 МБ"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Отключено"</item> - <item msgid="4195153527464162486">"Буфер: макс. 64 КБ"</item> - <item msgid="7464037639415220106">"Буфер: макс. 256 КБ"</item> - <item msgid="8539423820514360724">"Буфер: макс. 1 МБ"</item> - <item msgid="1984761927103140651">"Буфер: макс. 4 МБ"</item> - <item msgid="7892098981256010498">"Буфер: макс. 16 МБ"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Отключено"</item> <item msgid="6014837961827347618">"Все"</item> diff --git a/packages/SettingsLib/res/values-si/arrays.xml b/packages/SettingsLib/res/values-si/arrays.xml index 01d0dd2e845c..81d0bbbd9b8f 100644 --- a/packages/SettingsLib/res/values-si/arrays.xml +++ b/packages/SettingsLib/res/values-si/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", ක්රියාකාරී (මාධ්ය)"</item> <item msgid="5001852592115448348">", ක්රියාකාරී (දුරකථන)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"ක්රියාවිරහිතය"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"ක්රියාවිරහිතය"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"ක්රියාවිරහිතය"</item> - <item msgid="4195153527464162486">"ලොග අන්තරාවකට 64K"</item> - <item msgid="7464037639415220106">"ලොග අන්තරාවකට 256K"</item> - <item msgid="8539423820514360724">"ලොග අන්තරාවකට 1M"</item> - <item msgid="1984761927103140651">"ලොග අන්තරාවකට 4M"</item> - <item msgid="7892098981256010498">"ලොග අන්තරාවකට 16M"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"ක්රියාවිරහිතය"</item> <item msgid="6014837961827347618">"සියලු"</item> diff --git a/packages/SettingsLib/res/values-sk/arrays.xml b/packages/SettingsLib/res/values-sk/arrays.xml index 5dcf79106ec0..2826cb3191c2 100644 --- a/packages/SettingsLib/res/values-sk/arrays.xml +++ b/packages/SettingsLib/res/values-sk/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktívne (médiá)"</item> <item msgid="5001852592115448348">", aktívne (telefón)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Vypnuté"</item> - <item msgid="7839165897132179888">"64 kB"</item> - <item msgid="2715700596495505626">"256 kB"</item> - <item msgid="7099386891713159947">"1 MB"</item> - <item msgid="6069075827077845520">"4 MB"</item> - <item msgid="8243549501764402572">"16 MB"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Vypnuté"</item> <item msgid="4064786181089783077">"64 kB"</item> <item msgid="3052710745383602630">"256 kB"</item> <item msgid="3691785423374588514">"1 MB"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Vypnuté"</item> - <item msgid="4195153527464162486">"64 kB na vyrov. pamäť denníka"</item> - <item msgid="7464037639415220106">"256 kB na vyrov. pamäť denníka"</item> - <item msgid="8539423820514360724">"1 MB na vyrov. pam. denníka"</item> - <item msgid="1984761927103140651">"4 MB na vyrov. pamäť denníka"</item> - <item msgid="7892098981256010498">"16 MB na vyrov. pamäť denníka"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Vypnuté"</item> <item msgid="6014837961827347618">"Všetko"</item> diff --git a/packages/SettingsLib/res/values-sl/arrays.xml b/packages/SettingsLib/res/values-sl/arrays.xml index 7ba23affd24d..26c6e6eb5ab1 100644 --- a/packages/SettingsLib/res/values-sl/arrays.xml +++ b/packages/SettingsLib/res/values-sl/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktivno (predstavnost)"</item> <item msgid="5001852592115448348">", aktivno (telefon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Izklopljeno"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Izklopljeno"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Izklopljeno"</item> - <item msgid="4195153527464162486">"64 K/medpomnilnik dnevnika"</item> - <item msgid="7464037639415220106">"256 K/medpomnilnik dnevnika"</item> - <item msgid="8539423820514360724">"1 M/medpomnilnik dnevnika"</item> - <item msgid="1984761927103140651">"4 M/medpomnilnik dnevnika"</item> - <item msgid="7892098981256010498">"16 M/medpomnilnik dnevnika"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Izklopljeno"</item> <item msgid="6014837961827347618">"Vse"</item> diff --git a/packages/SettingsLib/res/values-sq/arrays.xml b/packages/SettingsLib/res/values-sq/arrays.xml index fccfc2fe4d7d..3db2e6b63690 100644 --- a/packages/SettingsLib/res/values-sq/arrays.xml +++ b/packages/SettingsLib/res/values-sq/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktiv (media)"</item> <item msgid="5001852592115448348">", aktiv (telefoni)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Joaktiv"</item> - <item msgid="7839165897132179888">"64 mijë"</item> - <item msgid="2715700596495505626">"256 mijë"</item> - <item msgid="7099386891713159947">"1 milion"</item> - <item msgid="6069075827077845520">"4 milionë"</item> - <item msgid="8243549501764402572">"16 milionë"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Joaktiv"</item> <item msgid="4064786181089783077">"64 mijë"</item> <item msgid="3052710745383602630">"256 mijë"</item> <item msgid="3691785423374588514">"1 milion"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Joaktiv"</item> - <item msgid="4195153527464162486">"64 mijë/memorie regjistrimi"</item> - <item msgid="7464037639415220106">"256 mijë/memorie regjistrimi"</item> - <item msgid="8539423820514360724">"1 milion/memorie regjistrimi"</item> - <item msgid="1984761927103140651">"4 milionë/memorie regjistrimi"</item> - <item msgid="7892098981256010498">"16 milionë/memorie regjistrimi"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Joaktive"</item> <item msgid="6014837961827347618">"Të gjitha"</item> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index b4e5db9baff4..d5c0231f7652 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -576,8 +576,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string> <string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string> <string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string> - <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) --> - <skip /> + <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Të dhënat celulare janë joaktive"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Nuk është caktuar të përdorë të dhënat"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Nuk ka telefon."</string> diff --git a/packages/SettingsLib/res/values-sr/arrays.xml b/packages/SettingsLib/res/values-sr/arrays.xml index 11b4b768897a..ec4da5a4a952 100644 --- a/packages/SettingsLib/res/values-sr/arrays.xml +++ b/packages/SettingsLib/res/values-sr/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", активан (медијски)"</item> <item msgid="5001852592115448348">", активан (телефон)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Искључено"</item> - <item msgid="7839165897132179888">"64 kB"</item> - <item msgid="2715700596495505626">"256 kB"</item> - <item msgid="7099386891713159947">"1 MB"</item> - <item msgid="6069075827077845520">"4 MB"</item> - <item msgid="8243549501764402572">"16 MB"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Искључено"</item> <item msgid="4064786181089783077">"64 kB"</item> <item msgid="3052710745383602630">"256 kB"</item> <item msgid="3691785423374588514">"1 MB"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Искључено"</item> - <item msgid="4195153527464162486">"64 kB по међумеморији евиденције"</item> - <item msgid="7464037639415220106">"256 kB по међумеморији евиденције"</item> - <item msgid="8539423820514360724">"1 MB по међумеморији евиденције"</item> - <item msgid="1984761927103140651">"4 MB по међумеморији евиденције"</item> - <item msgid="7892098981256010498">"16 MB по међумеморији евиденције"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Искључено"</item> <item msgid="6014837961827347618">"Све"</item> diff --git a/packages/SettingsLib/res/values-sv/arrays.xml b/packages/SettingsLib/res/values-sv/arrays.xml index be68c7156ad2..b631f44d6345 100644 --- a/packages/SettingsLib/res/values-sv/arrays.xml +++ b/packages/SettingsLib/res/values-sv/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktiv (media)"</item> <item msgid="5001852592115448348">", aktiv (telefon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Av"</item> - <item msgid="7839165897132179888">"64 kB"</item> - <item msgid="2715700596495505626">"256 kB"</item> - <item msgid="7099386891713159947">"1 MB"</item> - <item msgid="6069075827077845520">"4 MB"</item> - <item msgid="8243549501764402572">"16 MB"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Av"</item> <item msgid="4064786181089783077">"64 kB"</item> <item msgid="3052710745383602630">"256 kB"</item> <item msgid="3691785423374588514">"1 MB"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Av"</item> - <item msgid="4195153527464162486">"64 kB/loggbuffert"</item> - <item msgid="7464037639415220106">"256 kB/loggbuffert"</item> - <item msgid="8539423820514360724">"1 MB/loggbuffert"</item> - <item msgid="1984761927103140651">"4 MB/loggbuffert"</item> - <item msgid="7892098981256010498">"16 MB/loggbuffert"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Av"</item> <item msgid="6014837961827347618">"Alla"</item> diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml index da99b91d565b..cd15e2cb47d9 100644 --- a/packages/SettingsLib/res/values-sw/arrays.xml +++ b/packages/SettingsLib/res/values-sw/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", inatumika (maudhui)"</item> <item msgid="5001852592115448348">", inatumika (simu)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Imezimwa"</item> - <item msgid="7839165897132179888">"K64"</item> - <item msgid="2715700596495505626">"K256"</item> - <item msgid="7099386891713159947">"M1"</item> - <item msgid="6069075827077845520">"M4"</item> - <item msgid="8243549501764402572">"M16"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Imezimwa"</item> <item msgid="4064786181089783077">"K64"</item> <item msgid="3052710745383602630">"K256"</item> <item msgid="3691785423374588514">"M1"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Imezimwa"</item> - <item msgid="4195153527464162486">"K64 kwa kila akiba ya kumbukumbu"</item> - <item msgid="7464037639415220106">"K256 kwa kila akiba ya kumbukumbu"</item> - <item msgid="8539423820514360724">"M1 kwa kila akiba ya kumbukumbu"</item> - <item msgid="1984761927103140651">"M4 kwa kila akiba ya kumbukumbu"</item> - <item msgid="7892098981256010498">"M16 kwa kila akiba ya kumbukumbu"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Yamezimwa"</item> <item msgid="6014837961827347618">"Zote"</item> diff --git a/packages/SettingsLib/res/values-ta/arrays.xml b/packages/SettingsLib/res/values-ta/arrays.xml index 1c559545f92c..01b0a8e0c7c7 100644 --- a/packages/SettingsLib/res/values-ta/arrays.xml +++ b/packages/SettingsLib/res/values-ta/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", செயலில் உள்ளது (மீடியா)"</item> <item msgid="5001852592115448348">", செயலில் உள்ளது (மொபைல்)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"ஆஃப்"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"ஆஃப்"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"ஆஃப்"</item> - <item msgid="4195153527464162486">"64K / லாக் பஃபர்"</item> - <item msgid="7464037639415220106">"256K / லாக் பஃபர்"</item> - <item msgid="8539423820514360724">"1M / லாக் பஃபர்"</item> - <item msgid="1984761927103140651">"4M / லாக் பஃபர்"</item> - <item msgid="7892098981256010498">"16M / லாக் பஃபர்"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"ஆஃப்"</item> <item msgid="6014837961827347618">"எல்லாம்"</item> diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml index e1c0406ee168..4bdd55b20b68 100644 --- a/packages/SettingsLib/res/values-te/arrays.xml +++ b/packages/SettingsLib/res/values-te/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", (మీడియా) సక్రియంగా ఉంది"</item> <item msgid="5001852592115448348">", (ఫోన్) సక్రియంగా ఉంది"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"ఆఫ్"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"ఆఫ్"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"ఆఫ్ చేయబడింది"</item> - <item msgid="4195153527464162486">"లాగ్ బఫర్కి 64K"</item> - <item msgid="7464037639415220106">"లాగ్ బఫర్కి 256K"</item> - <item msgid="8539423820514360724">"లాగ్ బఫర్కి 1M"</item> - <item msgid="1984761927103140651">"లాగ్ బఫర్కి 4M"</item> - <item msgid="7892098981256010498">"లాగ్ బఫర్కి 16M"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"ఆఫ్ చేయి"</item> <item msgid="6014837961827347618">"అన్నీ"</item> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 33f082dc3f06..897abea211b9 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -576,8 +576,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string> <string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string> <string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string> - <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) --> - <skip /> + <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"మొబైల్ డేటా ఆఫ్లో ఉంది"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"డేటాను ఉపయోగించే విధంగా సెట్ చేయలేదు"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"ఫోన్ లేదు."</string> diff --git a/packages/SettingsLib/res/values-th/arrays.xml b/packages/SettingsLib/res/values-th/arrays.xml index 21fe6e4d3c9f..9f6080b0e581 100644 --- a/packages/SettingsLib/res/values-th/arrays.xml +++ b/packages/SettingsLib/res/values-th/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">"ใช้งานอยู่ (สื่อ)"</item> <item msgid="5001852592115448348">"ใช้งานอยู่ (โทรศัพท์)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"ปิด"</item> - <item msgid="7839165897132179888">"64 K"</item> - <item msgid="2715700596495505626">"256 K"</item> - <item msgid="7099386891713159947">"1 M"</item> - <item msgid="6069075827077845520">"4 M"</item> - <item msgid="8243549501764402572">"16 M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"ปิด"</item> <item msgid="4064786181089783077">"64 K"</item> <item msgid="3052710745383602630">"256 K"</item> <item msgid="3691785423374588514">"1 M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"ปิด"</item> - <item msgid="4195153527464162486">"64 K ต่อบัฟเฟอร์ไฟล์บันทึก"</item> - <item msgid="7464037639415220106">"256 K ต่อบัฟเฟอร์ไฟล์บันทึก"</item> - <item msgid="8539423820514360724">"1 M ต่อบัฟเฟอร์ไฟล์บันทึก"</item> - <item msgid="1984761927103140651">"4 M ต่อบัฟเฟอร์ไฟล์บันทึก"</item> - <item msgid="7892098981256010498">"16 M ต่อบัฟเฟอร์ไฟล์บันทึก"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"ปิด"</item> <item msgid="6014837961827347618">"ทั้งหมด"</item> diff --git a/packages/SettingsLib/res/values-tl/arrays.xml b/packages/SettingsLib/res/values-tl/arrays.xml index 9f07cff95c3b..ab68a6818483 100644 --- a/packages/SettingsLib/res/values-tl/arrays.xml +++ b/packages/SettingsLib/res/values-tl/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", aktibo (media)"</item> <item msgid="5001852592115448348">", aktibo (telepono)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"I-off"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"I-off"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"I-off"</item> - <item msgid="4195153527464162486">"64K kada log buffer"</item> - <item msgid="7464037639415220106">"256K kada log buffer"</item> - <item msgid="8539423820514360724">"1M kada log buffer"</item> - <item msgid="1984761927103140651">"4M kada log buffer"</item> - <item msgid="7892098981256010498">"16M kada log buffer"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Naka-off"</item> <item msgid="6014837961827347618">"Lahat"</item> diff --git a/packages/SettingsLib/res/values-tr/arrays.xml b/packages/SettingsLib/res/values-tr/arrays.xml index fc90c9abbee7..d5d578ce4ff2 100644 --- a/packages/SettingsLib/res/values-tr/arrays.xml +++ b/packages/SettingsLib/res/values-tr/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", etkin (medya)"</item> <item msgid="5001852592115448348">", etkin (telefon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Kapalı"</item> - <item msgid="7839165897132179888">"64 KB"</item> - <item msgid="2715700596495505626">"256 KB"</item> - <item msgid="7099386891713159947">"1 MB"</item> - <item msgid="6069075827077845520">"4 MB"</item> - <item msgid="8243549501764402572">"16 MB"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Kapalı"</item> <item msgid="4064786181089783077">"64 KB"</item> <item msgid="3052710745383602630">"256 KB"</item> <item msgid="3691785423374588514">"1 MB"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Kapalı"</item> - <item msgid="4195153527464162486">"Günlük arabelleği başına 64 KB"</item> - <item msgid="7464037639415220106">"Günlük arabelleği başına 256 KB"</item> - <item msgid="8539423820514360724">"Günlük arabelleği başına 1 MB"</item> - <item msgid="1984761927103140651">"Günlük arabelleği başına 4 MB"</item> - <item msgid="7892098981256010498">"Günlük arabelleği başına 16 MB"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Kapalı"</item> <item msgid="6014837961827347618">"Tümü"</item> diff --git a/packages/SettingsLib/res/values-uk/arrays.xml b/packages/SettingsLib/res/values-uk/arrays.xml index 4405c37c7c25..41922a3de961 100644 --- a/packages/SettingsLib/res/values-uk/arrays.xml +++ b/packages/SettingsLib/res/values-uk/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", активний (лише для медіа)"</item> <item msgid="5001852592115448348">", активний (лише для телефона)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Вимкнено"</item> - <item msgid="7839165897132179888">"64 КБ"</item> - <item msgid="2715700596495505626">"256 КБ"</item> - <item msgid="7099386891713159947">"1 МБ"</item> - <item msgid="6069075827077845520">"4 МБ"</item> - <item msgid="8243549501764402572">"16 МБ"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Вимкнено"</item> <item msgid="4064786181089783077">"64 КБ"</item> <item msgid="3052710745383602630">"256 КБ"</item> <item msgid="3691785423374588514">"1 МБ"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Вимкнено"</item> - <item msgid="4195153527464162486">"Буфер журналу: 64 КБ"</item> - <item msgid="7464037639415220106">"Буфер журналу: 256 КБ"</item> - <item msgid="8539423820514360724">"Буфер журналу: 1 МБ"</item> - <item msgid="1984761927103140651">"Буфер журналу: 4 МБ"</item> - <item msgid="7892098981256010498">"Буфер журналу: 16 МБ"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Вимкнено"</item> <item msgid="6014837961827347618">"Усі"</item> diff --git a/packages/SettingsLib/res/values-ur/arrays.xml b/packages/SettingsLib/res/values-ur/arrays.xml index f4c2500d28e7..a3539ff1f84e 100644 --- a/packages/SettingsLib/res/values-ur/arrays.xml +++ b/packages/SettingsLib/res/values-ur/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">"، فعال (میڈیا)"</item> <item msgid="5001852592115448348">"، فعال (فون)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"آف"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"آف"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"آف"</item> - <item msgid="4195153527464162486">"64K فی لاگ بفر"</item> - <item msgid="7464037639415220106">"256K فی لاگ بفر"</item> - <item msgid="8539423820514360724">"1M فی لاگ بفر"</item> - <item msgid="1984761927103140651">"4M فی لاگ بفر"</item> - <item msgid="7892098981256010498">"16M فی لاگ بفر"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"آف"</item> <item msgid="6014837961827347618">"تمام"</item> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 9376bdcaa4df..82783b3a172a 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -576,8 +576,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string> <string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string> <string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string> - <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) --> - <skip /> + <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"موبائل ڈیٹا آف ہے"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"ڈیٹا استعمال کرنے کے لیے سیٹ نہیں ہے"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"کوئی فون نہیں ہے۔"</string> diff --git a/packages/SettingsLib/res/values-uz/arrays.xml b/packages/SettingsLib/res/values-uz/arrays.xml index 0770dcc42bc8..e695e20a3db4 100644 --- a/packages/SettingsLib/res/values-uz/arrays.xml +++ b/packages/SettingsLib/res/values-uz/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", faol (media)"</item> <item msgid="5001852592115448348">", faol (telefon)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Yoqilmagan"</item> - <item msgid="7839165897132179888">"64 KB"</item> - <item msgid="2715700596495505626">"256 KB"</item> - <item msgid="7099386891713159947">"1 MB"</item> - <item msgid="6069075827077845520">"4 MB"</item> - <item msgid="8243549501764402572">"16 MB"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Yoqilmagan"</item> <item msgid="4064786181089783077">"64 KB"</item> <item msgid="3052710745383602630">"256 KB"</item> <item msgid="3691785423374588514">"1 MB"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Yoqilmagan"</item> - <item msgid="4195153527464162486">"Bufer: maks. 64 KB"</item> - <item msgid="7464037639415220106">"Bufer: maks. 256 KB"</item> - <item msgid="8539423820514360724">"Bufer: maks. 1 MB"</item> - <item msgid="1984761927103140651">"Bufer: maks. 4 MB"</item> - <item msgid="7892098981256010498">"Bufer: maks. 16 MB"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Yoqilmagan"</item> <item msgid="6014837961827347618">"Hammasi"</item> diff --git a/packages/SettingsLib/res/values-vi/arrays.xml b/packages/SettingsLib/res/values-vi/arrays.xml index 635cf115c704..cac6c46ab520 100644 --- a/packages/SettingsLib/res/values-vi/arrays.xml +++ b/packages/SettingsLib/res/values-vi/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", đang hoạt động (nội dung nghe nhìn)"</item> <item msgid="5001852592115448348">", đang hoạt động (điện thoại)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Tắt"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Tắt"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Tắt"</item> - <item msgid="4195153527464162486">"64K mỗi bộ đệm nhật ký"</item> - <item msgid="7464037639415220106">"256K mỗi bộ đệm nhật ký"</item> - <item msgid="8539423820514360724">"1M mỗi bộ đệm nhật ký"</item> - <item msgid="1984761927103140651">"4M mỗi bộ đệm nhật ký"</item> - <item msgid="7892098981256010498">"16M mỗi bộ đệm nhật ký"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Tắt"</item> <item msgid="6014837961827347618">"Tất cả"</item> diff --git a/packages/SettingsLib/res/values-zh-rCN/arrays.xml b/packages/SettingsLib/res/values-zh-rCN/arrays.xml index 29d04e9cfda4..dc0ca10f076e 100644 --- a/packages/SettingsLib/res/values-zh-rCN/arrays.xml +++ b/packages/SettingsLib/res/values-zh-rCN/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">",使用中(媒体)"</item> <item msgid="5001852592115448348">",使用中(手机)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"关闭"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"关闭"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"关闭"</item> - <item msgid="4195153527464162486">"每个日志缓冲区 64K"</item> - <item msgid="7464037639415220106">"每个日志缓冲区 256K"</item> - <item msgid="8539423820514360724">"每个日志缓冲区 1M"</item> - <item msgid="1984761927103140651">"每个日志缓冲区 4M"</item> - <item msgid="7892098981256010498">"每个日志缓冲区 16M"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"关闭"</item> <item msgid="6014837961827347618">"全部"</item> diff --git a/packages/SettingsLib/res/values-zh-rHK/arrays.xml b/packages/SettingsLib/res/values-zh-rHK/arrays.xml index e7e2f845caf8..cae08a65b420 100644 --- a/packages/SettingsLib/res/values-zh-rHK/arrays.xml +++ b/packages/SettingsLib/res/values-zh-rHK/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">",使用中 (媒體)"</item> <item msgid="5001852592115448348">",使用中 (手機)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"關閉"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"關閉"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"關閉"</item> - <item msgid="4195153527464162486">"每個記錄緩衝區 64K"</item> - <item msgid="7464037639415220106">"每個記錄緩衝區 256K"</item> - <item msgid="8539423820514360724">"每個記錄緩衝區 1M"</item> - <item msgid="1984761927103140651">"每個記錄緩衝區 4M"</item> - <item msgid="7892098981256010498">"每個記錄緩衝區 16M"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"關閉"</item> <item msgid="6014837961827347618">"全部"</item> diff --git a/packages/SettingsLib/res/values-zh-rTW/arrays.xml b/packages/SettingsLib/res/values-zh-rTW/arrays.xml index 0fdc14e4bf33..959d022ce784 100644 --- a/packages/SettingsLib/res/values-zh-rTW/arrays.xml +++ b/packages/SettingsLib/res/values-zh-rTW/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">",使用中 (媒體)"</item> <item msgid="5001852592115448348">",使用中 (手機)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"關閉"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"關閉"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"關閉"</item> - <item msgid="4195153527464162486">"每個記錄緩衝區 64K"</item> - <item msgid="7464037639415220106">"每個記錄緩衝區 256K"</item> - <item msgid="8539423820514360724">"每個記錄緩衝區 1M"</item> - <item msgid="1984761927103140651">"每個記錄緩衝區 4M"</item> - <item msgid="7892098981256010498">"每個記錄緩衝區 16M"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"關閉"</item> <item msgid="6014837961827347618">"全部"</item> diff --git a/packages/SettingsLib/res/values-zu/arrays.xml b/packages/SettingsLib/res/values-zu/arrays.xml index 2d43c6791c95..78079e81f2ae 100644 --- a/packages/SettingsLib/res/values-zu/arrays.xml +++ b/packages/SettingsLib/res/values-zu/arrays.xml @@ -155,28 +155,14 @@ <item msgid="253388653486517049">", iyasebenza (imidiya)"</item> <item msgid="5001852592115448348">", iyasebenza (ifoni)"</item> </string-array> - <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Valiwe"</item> - <item msgid="7839165897132179888">"64K"</item> - <item msgid="2715700596495505626">"256K"</item> - <item msgid="7099386891713159947">"1M"</item> - <item msgid="6069075827077845520">"4M"</item> - <item msgid="8243549501764402572">"16M"</item> - </string-array> + <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Valiwe"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Valiwe"</item> - <item msgid="4195153527464162486">"64K ngebhafa yelogu ngayinye"</item> - <item msgid="7464037639415220106">"256K ngebhafa yelogu ngayinye"</item> - <item msgid="8539423820514360724">"1M ngebhafa yelogu ngayi"</item> - <item msgid="1984761927103140651">"4M ngebhafa yelogu ngayinye"</item> - <item msgid="7892098981256010498">"16M ngebhafa yelogu ngayinye"</item> - </string-array> + <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Valiwe"</item> <item msgid="6014837961827347618">"Konke"</item> diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index d278c5974ae6..86aa214b04bf 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -83,7 +83,7 @@ public class SystemSettingsValidators { return value == null || value.length() < MAX_LENGTH; } }); - VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.85f, 1.3f)); + VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.25f, 5.0f)); VALIDATORS.put(System.DIM_SCREEN, BOOLEAN_VALIDATOR); VALIDATORS.put( System.DISPLAY_COLOR_MODE, diff --git a/packages/Shell/Android.bp b/packages/Shell/Android.bp index c873e30d2107..546642db881a 100644 --- a/packages/Shell/Android.bp +++ b/packages/Shell/Android.bp @@ -1,11 +1,15 @@ +// used both for the android_app and android_library +shell_srcs = ["src/**/*.java",":dumpstate_aidl"] +shell_static_libs = ["androidx.legacy_legacy-support-v4"] + android_app { name: "Shell", defaults: ["platform_app_defaults"], - srcs: ["src/**/*.java",":dumpstate_aidl"], + srcs: shell_srcs, aidl: { include_dirs: ["frameworks/native/cmds/dumpstate/binder"], }, - static_libs: ["androidx.legacy_legacy-support-v4"], + static_libs: shell_static_libs, platform_apis: true, certificate: "platform", privileged: true, @@ -13,3 +17,18 @@ android_app { include_filter: ["com.android.shell.*"], }, } + +// A library for product type like auto to create a new shell package +// with product specific permissions. +android_library { + name: "Shell-package-library", + defaults: ["platform_app_defaults"], + srcs: shell_srcs, + aidl: { + include_dirs: ["frameworks/native/cmds/dumpstate/binder"], + }, + resource_dirs: ["res"], + static_libs: shell_static_libs, + platform_apis: true, + manifest: "AndroidManifest.xml", +} diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 41cc835ead82..859f5a68f266 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -119,6 +119,7 @@ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> <uses-permission android:name="android.permission.CREATE_USERS" /> + <uses-permission android:name="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP" /> <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" /> <uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" /> <uses-permission android:name="android.permission.ACCESS_LOWPAN_STATE"/> diff --git a/packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml b/packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml index 3790378ff3ae..8e3768697f6a 100644 --- a/packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml +++ b/packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml @@ -17,9 +17,8 @@ <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/media_seamless_border"> <item android:id="@android:id/background"> - <shape - android:color="@android:color/transparent"> - <stroke android:width="1dp" android:color="@color/media_seamless_border"/> + <shape android:shape="rectangle"> + <solid android:color="@color/media_seamless_border" /> <corners android:radius="24dp"/> </shape> </item> diff --git a/packages/SystemUI/res/drawable/ic_camera_blocked.xml b/packages/SystemUI/res/drawable/ic_camera_blocked.xml new file mode 100644 index 000000000000..0161bcbd1937 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_camera_blocked.xml @@ -0,0 +1,29 @@ +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="m18,12c-2.75,0 -5,2.25 -5,5 0,2.75 2.25,5 5,5 2.75,0 5,-2.25 5,-5 0,-2.75 -2.1667,-5 -5,-5zM15.5,17.8333h5v-1.6666h-5z" + android:fillColor="#30302a" + android:fillType="evenOdd"/> + <path + android:pathData="m16.4,5.5004h-2.536l-1.464,-1.6H7.6l-1.464,1.6H3.6c-0.88,0 -1.6,0.72 -1.6,1.6v9.6c0,0.88 0.72,1.6 1.6,1.6h8.5413C12.0488,17.8817 12,17.4465 12,17c0,-0.1005 0.0025,-0.2004 0.0073,-0.2996H3.6V7.1004H16.4V11.2157C16.9094,11.0751 17.4459,11 18,11V7.1004c0,-0.88 -0.72,-1.6 -1.6,-1.6zM6.8,11.9004c0,-1.768 1.432,-3.2 3.2,-3.2 1.768,0 3.2,1.432 3.2,3.2 0,1.768 -1.432,3.2 -3.2,3.2 -1.768,0 -3.2,-1.432 -3.2,-3.2z" + android:fillColor="#30302a" + android:fillType="evenOdd"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_mic_blocked.xml b/packages/SystemUI/res/drawable/ic_mic_blocked.xml new file mode 100644 index 000000000000..0ce7a581a7e5 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_mic_blocked.xml @@ -0,0 +1,29 @@ +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="m17,12c-2.75,0 -5,2.25 -5,5 0,2.75 2.25,5 5,5 2.75,0 5,-2.25 5,-5 0,-2.75 -2.1667,-5 -5,-5zM14.5,17.8333h5v-1.6666h-5z" + android:fillColor="#30302a" + android:fillType="evenOdd"/> + <path + android:pathData="m12,12c0,1.66 -1.34,3 -3,3C7.34,15 6,13.66 6,12L6,6C6,4.34 7.34,3 9,3c1.66,0 3,1.34 3,3zM9,5C8.45,5 8,5.45 8,6v6c0,0.55 0.45,1 1,1 0.55,0 1,-0.45 1,-1L10,6C10,5.45 9.55,5 9,5ZM11.0147,16.577C10.3983,16.849 9.7167,17 9,17 6.24,17 4,14.76 4,12L2,12c0,3.53 2.61,6.43 6,6.92L8,22h2v-3.08c0.4212,-0.0609 0.8303,-0.1589 1.2238,-0.2908C11.078,18.1111 11,17.5647 11,17c0,-0.1422 0.0049,-0.2832 0.0147,-0.423z" + android:fillColor="#30302a" + android:fillType="evenOdd"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_no_calling_sms.xml b/packages/SystemUI/res/drawable/ic_qs_no_calling_sms.xml new file mode 100644 index 000000000000..0e308d2f5bfb --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_no_calling_sms.xml @@ -0,0 +1,12 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M20.17,14.84l-3.26,-0.65c-0.33,-0.07 -0.67,0.04 -0.9,0.27l-2.62,2.62c-2.75,-1.49 -5.01,-3.75 -6.5,-6.5l2.62,-2.62c0.24,-0.24 0.34,-0.58 0.27,-0.9L9.13,3.8C9.04,3.34 8.63,3 8.15,3H4C3.44,3 2.97,3.47 3,4.03c0.17,2.91 1.04,5.63 2.43,8.01c1.57,2.69 3.81,4.93 6.5,6.5c2.38,1.39 5.1,2.26 8.01,2.43c0.56,0.03 1.03,-0.44 1.03,-1v-4.15C20.97,15.34 20.64,14.93 20.17,14.84z"/> + <path + android:fillColor="#FF000000" + android:pathData="M22,3.41L20.59,2L18.5,4.09L16.41,2L15,3.41l2.09,2.09L15,7.59L16.41,9l2.09,-2.08L20.59,9L22,7.59L19.92,5.5L22,3.41z"/> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/qs_media_background.xml b/packages/SystemUI/res/drawable/qs_media_background.xml index 656d2e4a33ff..6ed3a0aed091 100644 --- a/packages/SystemUI/res/drawable/qs_media_background.xml +++ b/packages/SystemUI/res/drawable/qs_media_background.xml @@ -17,4 +17,4 @@ <com.android.systemui.media.IlluminationDrawable xmlns:systemui="http://schemas.android.com/apk/res-auto" systemui:highlight="15" - systemui:cornerRadius="?android:attr/dialogCornerRadius" />
\ No newline at end of file + systemui:cornerRadius="@dimen/notification_corner_radius" />
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml index 170f2c4e0bea..6b4270531d0b 100644 --- a/packages/SystemUI/res/layout/media_view.xml +++ b/packages/SystemUI/res/layout/media_view.xml @@ -64,7 +64,7 @@ </FrameLayout> <!-- Actions must be ordered left-to-right even in RTL layout. However, they appear in a chain - with the album art and the title, and must as a group appear at the end of that chain. This is + with the album art, and must as a group appear at the end of that chain. This is accomplished by having all actions appear in a LTR chain within the parent, and then biasing it to the right side, then this barrier is used to bound the text views. --> <androidx.constraintlayout.widget.Barrier @@ -72,10 +72,10 @@ android:layout_width="0dp" android:layout_height="0dp" android:orientation="vertical" - app:layout_constraintTop_toTopOf="parent" + app:layout_constraintTop_toBottomOf="@id/header_artist" app:barrierDirection="start" app:constraint_referenced_ids="action0,action1,action2,action3,action4" - /> + app:layout_constraintHorizontal_bias="0" /> <ImageButton android:id="@+id/action0" @@ -111,42 +111,46 @@ <ImageView android:id="@+id/album_art" android:layout_width="@dimen/qs_media_album_size" - android:layout_height="@dimen/qs_media_album_size" /> + android:layout_height="@dimen/qs_media_album_size" + android:layout_gravity="center_vertical" /> <!-- Seamless Output Switcher --> <LinearLayout android:id="@+id/media_seamless" android:layout_width="0dp" android:layout_height="wrap_content" - android:foreground="@drawable/qs_media_seamless_background" - android:background="@drawable/qs_media_light_source" android:orientation="horizontal" - android:forceHasOverlappingRendering="false" - android:paddingStart="12dp" - android:paddingTop="6dp" - android:paddingEnd="12dp" - android:paddingBottom="6dp"> - - <ImageView - android:id="@+id/media_seamless_image" - android:layout_width="@dimen/qs_seamless_icon_size" - android:layout_height="@dimen/qs_seamless_icon_size" - android:layout_marginEnd="8dp" - android:layout_gravity="center_vertical" - android:tint="@color/media_primary_text" - android:src="@*android:drawable/ic_media_seamless" /> - - <TextView - android:id="@+id/media_seamless_text" + android:gravity="center_vertical|end" + android:forceHasOverlappingRendering="false"> + <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:fontFamily="@*android:string/config_headlineFontFamily" - android:singleLine="true" - android:text="@*android:string/ext_media_seamless_action" - android:textColor="@color/media_primary_text" - android:textDirection="locale" - android:textSize="14sp" /> + android:foreground="@drawable/qs_media_seamless_background" + android:background="@drawable/qs_media_light_source" + android:orientation="horizontal" + android:padding="6dp" + android:contentDescription="@string/quick_settings_media_device_label"> + <ImageView + android:id="@+id/media_seamless_image" + android:layout_width="@dimen/qs_seamless_icon_size" + android:layout_height="@dimen/qs_seamless_icon_size" + android:layout_gravity="center" + android:tint="@color/media_primary_text" + android:src="@*android:drawable/ic_media_seamless" /> + <TextView + android:visibility="gone" + android:id="@+id/media_seamless_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_marginStart="8dp" + android:fontFamily="@*android:string/config_headlineFontFamily" + android:singleLine="true" + android:text="@*android:string/ext_media_seamless_action" + android:textColor="@color/media_primary_text" + android:textDirection="locale" + android:textSize="14sp" /> + </LinearLayout> </LinearLayout> <ImageView @@ -206,8 +210,9 @@ <com.android.internal.widget.CachingIconView android:id="@+id/icon" android:tint="@color/media_primary_text" - android:layout_width="@dimen/qs_media_icon_size" - android:layout_height="@dimen/qs_media_icon_size" /> + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_margin="6dp" /> <!-- Constraints are set here as they are the same regardless of host --> <TextView diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 317e0e89118b..ba39d1e38ac8 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Invoermetode"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Ligging"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Ligging af"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Mediatoestel"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Net noodoproepe"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index b7b24db814b4..a193bbbec03e 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"የግቤት ስልት"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"አካባቢ"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"አካባቢ ጠፍቷል"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"የሚዲያ መሣሪያ"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"የአደጋ ጊዜ ጥሪዎች ብቻ"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index ca823eda6e60..a634281dceb7 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -348,6 +348,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"أسلوب الإدخال"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"الموقع الجغرافي"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"الموقع قيد الإيقاف"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"جهاز الوسائط"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"مكالمات طوارئ فقط"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 8e6fd519f05f..1151644fd849 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"ইনপুট পদ্ধতি"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"অৱস্থান"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"অৱস্থান অফ"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"মিডিয়া ডিভাইচ"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"জৰুৰীকালীন কল মাত্ৰ"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index d9a2ae17f1b4..146add2f5319 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Daxiletmə metodu"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Yer"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Yer Deaktiv"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media cihazı"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Yalnız təcili zənglər"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index ef3a1cf27515..77b101c392ed 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -345,6 +345,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Metod unosa"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Lokacija je isključena"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medijski uređaj"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Samo hitni pozivi"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index a1804d12ab76..5977d043eb32 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -346,6 +346,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Метад уводу"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Месцазнаходжанне"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Вызначэнне месцазнаходжання адключана"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Мультымедыйная прылада"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Толькі экстранныя выклікі"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 6b59b2484f6f..53eeac81ef0d 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Метод на въвеждане"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Местоположение"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Местоположението е изключено"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Мултимедийно устройство"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"Индикатор за силата на получения сигнал (RSSI)"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Само спешни обаждания"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index a6344d0c45b0..b944dde288b0 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"ইনপুট পদ্ধতি"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"লোকেশন"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"লোকেশন বন্ধ করা আছে"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"মিডিয়া ডিভাইস"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"শুধুমাত্র জরুরি কল"</string> @@ -354,12 +358,9 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"নতুন ব্যবহারকারী"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ওয়াই-ফাই"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"ইন্টারনেট"</string> - <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) --> - <skip /> - <!-- no translation found for quick_settings_networks_available (1875138606855420438) --> - <skip /> - <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) --> - <skip /> + <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"বিমানের জন্য নিরাপদ"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"নেটওয়ার্ক উপলভ্য"</string> + <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"নেটওয়ার্ক উপলভ্য নেই"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"সংযুক্ত নয়"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"কোনো নেটওয়ার্ক নেই"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ওয়াই-ফাই বন্ধ"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 0dc97fabeae1..7717c653bc09 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -345,6 +345,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Način unosa"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Utvrđivanje lokacije isključeno"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medijski uređaj"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Samo pozivi za hitne slučajeve"</string> @@ -355,12 +359,9 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> - <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) --> - <skip /> - <!-- no translation found for quick_settings_networks_available (1875138606855420438) --> - <skip /> - <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) --> - <skip /> + <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Sigurno za rad u zrakoplovu"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"Mreže su dostupne"</string> + <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Mreže nisu dostupne"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nije povezano"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nema mreže"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi je isključen"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index bd5815a6c675..f648bc6a0c94 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Mètode d\'introducció"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicació"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Ubicació desactivada"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositiu multimèdia"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Només trucades d\'emergència"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 41a33ef0d51b..4d14d2b41386 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -346,6 +346,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Metoda zadávání dat"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Poloha"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Poloha vypnuta"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Mediální zařízení"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Pouze tísňová volání"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 3d8b027396dc..95b1a55206c0 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Inputmetode"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Placering"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Placering fra"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medieenhed"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Kun nødopkald"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index a0e535fb4ca2..e6acd87f6cb3 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Eingabemethode"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Standort"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Standort aus"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Mediengerät"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Nur Notrufe"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 13dafa019f72..867c7d200474 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Μέθοδος εισαγωγής"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Τοποθεσία"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Τοποθεσία απενεργοποιημένη"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Συσκευή μέσων"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Μόνο κλήσεις έκτακτης ανάγκης"</string> @@ -354,12 +358,9 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Νέος χρήστης"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"Διαδίκτυο"</string> - <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) --> - <skip /> - <!-- no translation found for quick_settings_networks_available (1875138606855420438) --> - <skip /> - <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) --> - <skip /> + <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Ασφαλές για πτήση"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"Υπάρχουν διαθέσιμα δίκτυα"</string> + <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Δεν υπάρχουν διαθέσιμα δίκτυα"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Μη συνδεδεμένο"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Κανένα δίκτυο"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ανενεργό"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index dcee89d5ef4b..185af57d12cd 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Input Method"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Location Off"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media device"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Emergency Calls Only"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 3217d1c058ad..3517f878d512 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Input Method"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Location Off"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media device"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Emergency Calls Only"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index dcee89d5ef4b..185af57d12cd 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Input Method"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Location Off"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media device"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Emergency Calls Only"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index dcee89d5ef4b..185af57d12cd 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Input Method"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Location Off"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media device"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Emergency Calls Only"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 00fa58477291..306116cf3e36 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Input Method"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Location Off"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media device"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Emergency Calls Only"</string> @@ -354,12 +358,9 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> - <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) --> - <skip /> - <!-- no translation found for quick_settings_networks_available (1875138606855420438) --> - <skip /> - <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) --> - <skip /> + <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Airplane-safe"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"Networks available"</string> + <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Networks unavailable"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index a552c7647333..27c424bdc6e9 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Método de introducción"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicación"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Ubicación desactivada"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo multimedia"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Solo emergencia"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 4277c6316922..ab5d0d2440d2 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Método de entrada"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicación"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Ubicación desactivada"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo multimedia"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Solo llamadas de emergencia"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 64299318c819..6c7e19a2a6ae 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Sisestusmeetod"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Asukoht"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Asukoht on väljas"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Meediaseade"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Ainult hädaabikõned"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 664ae9816066..da7733c59632 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Idazketa-metodoa"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Kokapena"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Kokapena desaktibatuta"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Multimedia-gailua"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Larrialdi-deiak soilik"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 578fe887619d..2bc2e570d8d2 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"روش ورودی"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"مکان"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"مکان خاموش"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"دستگاه رسانه"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"فقط تماسهای اضطراری"</string> @@ -354,12 +358,9 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"کاربر جدید"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"اینترنت"</string> - <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) --> - <skip /> - <!-- no translation found for quick_settings_networks_available (1875138606855420438) --> - <skip /> - <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) --> - <skip /> + <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"ایمن در هواپیما"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"شبکه دردسترس است"</string> + <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"شبکه دردسترس نیست"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"متصل نیست"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"شبکهای موجود نیست"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi خاموش است"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index d73b2eb7b1ac..28708b945500 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Syöttötapa"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Sijainti"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Sijainti ei käytössä"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medialaite"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Vain hätäpuhelut"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index df0e36c7d364..8235638c14e2 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Mode de saisie"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Position"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Localisation désactivée"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Appareil multimédia"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Appels d\'urgence uniquement"</string> @@ -354,12 +358,9 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nouvel utilisateur"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> - <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) --> - <skip /> - <!-- no translation found for quick_settings_networks_available (1875138606855420438) --> - <skip /> - <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) --> - <skip /> + <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Sécuritaire pour les avions"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"Réseaux accessibles"</string> + <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Aucun réseau accessible"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non connecté"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Aucun réseau"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi désactivé"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 64a232874f42..50a3cfbf442a 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Mode de saisie"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Localisation désactivée"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Appareil multimédia"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Appels d\'urgence"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 75db845b2e0f..4c6a53ebc589 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Método de introdución de texto"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Localización"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Localización desactivada"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo multimedia"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Só chamadas de emerxencia"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index c0d2557a45a6..add1ef524811 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"ઇનપુટ પદ્ધતિ"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"સ્થાન"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"સ્થાન બંધ"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"મીડિયા ઉપકરણ"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"ફક્ત ઇમર્જન્સી કૉલ"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 0bfa5aef27c6..b4d61bbe6629 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -346,6 +346,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"इनपुट विधि"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"जगह"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"जगह की जानकारी बंद है"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"मीडिया डिवाइस"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"सिर्फ़ आपातकालीन कॉल"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 33d0534d47c6..e5cf20a22f55 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -345,6 +345,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Način unosa"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Lokacija je isključena"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medijski uređaj"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Samo hitni pozivi"</string> @@ -355,12 +359,9 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> - <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) --> - <skip /> - <!-- no translation found for quick_settings_networks_available (1875138606855420438) --> - <skip /> - <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) --> - <skip /> + <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Sigurno za rad u zrakoplovu"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"Mreže su dostupne"</string> + <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Mreže nisu dostupne"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nije povezano"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nema mreže"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi isključen"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 22b8fde2ff51..958e25f696d9 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Beviteli módszer"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Tartózkodási hely"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Hely kikapcsolva"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Médiaeszköz"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Csak segélyhívások"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 5e51c627f88b..4fd1abe78fc4 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Մուտքագրման եղանակը"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Տեղորոշում"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Անջատել տեղադրությունը"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Մեդիա սարք"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Միայն շտապ կանչեր"</string> @@ -354,12 +358,9 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Նոր օգտատեր"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"Ինտերնետ"</string> - <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) --> - <skip /> - <!-- no translation found for quick_settings_networks_available (1875138606855420438) --> - <skip /> - <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) --> - <skip /> + <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Ցանցեր, որոնք անվտանգ են ինքնաթիռում"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"Հասանելի ցանցեր"</string> + <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Անհասանելի ցանցեր"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Միացված չէ"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ցանց չկա"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi-ը անջատված է"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 9a217ce0c966..4136ead36ee9 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Metode Masukan"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasi"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Lokasi Nonaktif"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Perangkat media"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Panggilan Darurat Saja"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 3f8e8fb68c06..15c07e428ca5 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Innsláttaraðferð"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Staðsetning"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Staðsetning óvirk"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Margmiðlunartæki"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Aðeins neyðarsímtöl"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 72cc5aadefd1..299824b1aa7e 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Metodo di immissione"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Geolocalizzazione"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Geolocalizz. non attiva"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo multimediale"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Solo chiamate di emergenza"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index d8c148aada43..e8cc8bfcd7bc 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -346,6 +346,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"שיטת קלט"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"מיקום"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"מיקום כבוי"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"מכשיר מדיה"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"שיחות חירום בלבד"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 40a93231f3a1..459b52037ff1 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"入力方法"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"位置情報"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"現在地OFF"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"メディアデバイス"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"緊急通報のみ"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 6ab551015704..20f2d03e6a86 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"შეყვანის მეთოდი"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"მდებარეობა"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"მდებარეობა გამორთულია"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"მედია მოწყობილობა"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"მხოლოდ გადაუდებელი დახმარების ზარებისთვის"</string> @@ -354,12 +358,9 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ახალი მომხმარებელი"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"ინტერნეტი"</string> - <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) --> - <skip /> - <!-- no translation found for quick_settings_networks_available (1875138606855420438) --> - <skip /> - <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) --> - <skip /> + <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"თვითმფრინავისთვის უსაფრთხო"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"ქსელები ხელმისაწვდომია"</string> + <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"ქსელები მიუწვდომელია"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"არ არის დაკავშირებული."</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ქსელი არ არის"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi გამორთულია"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index bda2f1a6b6c4..65f303283fe1 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Енгізу әдісі"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Орналасу"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Орын өшірулі"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Meдиа құрылғысы"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI (алынған сигнал қуатының көрсеткіші)"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Құтқару қызметіне ғана қоңырау шалынады"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 275a73a59155..a4f6e0a3b801 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"វិធីសាស្ត្របញ្ចូល"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ទីតាំង"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"ទីតាំងបានបិទ"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"ឧបករណ៍មេឌៀ"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"ការហៅទៅលេខសង្គ្រោះបន្ទាន់តែប៉ុណ្ណោះ"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 778ccca0c8cf..e02ed5a44ffb 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"ಇನ್ಪುಟ್ ವಿಧಾನ"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ಸ್ಥಳ"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"ಸ್ಥಳ ಆಫ್ ಆಗಿದೆ"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"ಮಾಧ್ಯಮ ಸಾಧನ"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"ತುರ್ತು ಕರೆಗಳು ಮಾತ್ರ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index e602ede4e1ad..e9d33fa70d72 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"입력 방법"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"위치"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"위치 사용 중지"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"미디어 기기"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"긴급 통화만 허용"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 0a2e8aeb9e02..f4191d4b9add 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -346,6 +346,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Киргизүү ыкмасы"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Жайгашкан жер"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Жайгашытрууну өчүрүү"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Медиа түзмөгү"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Кырсыктаганда гана чалуу"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 0f320a50b88b..0f0236fb2d60 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"ວິທີການປ້ອນຂໍ້ມູນ"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ສະຖານທີ່"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"ຂໍ້ມູນສະຖານທີ່ປິດຢູ່"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"ອຸປະກອນສື່"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"ໂທສຸກເສີນເທົ່ານັ້ນ"</string> @@ -354,12 +358,9 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ຜູ່ໃຊ້ໃໝ່"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"ອິນເຕີເນັດ"</string> - <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) --> - <skip /> - <!-- no translation found for quick_settings_networks_available (1875138606855420438) --> - <skip /> - <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) --> - <skip /> + <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"ປອດໄພກັບໃນຍົນ"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"ມີເຄືອຂ່າຍທີ່ສາມາດໃຊ້ໄດ້"</string> + <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"ບໍ່ມີເຄືອຂ່າຍທີ່ສາມາດໃຊ້ໄດ້"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ບໍ່ໄດ້ເຊື່ອມຕໍ່"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ບໍ່ມີເຄືອຂ່າຍ"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ປິດ"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 29f615eb022b..8d9051de4213 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -346,6 +346,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Įvesties metodas"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Vietovė"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Vietovė išjungta"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medijos įrenginys"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Tik skambučiai pagalbos numeriu"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index f209ba836e0a..1cde5323ef6c 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -345,6 +345,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Ievades metode"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Atrašanās vieta"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Atrašanās vieta izslēgta"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Multivides ierīce"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Tikai ārkārtas izsaukumi"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 001e6a0514a9..a006bc97557b 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Метод на внес"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Локација"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Исклучи локација"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Медиумски уред"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Само итни повици"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index e2ca3bbc61c7..4d2898fc131f 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"ടൈപ്പുചെയ്യൽ രീതി"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ലൊക്കേഷൻ"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"ലൊക്കേഷൻ ഓഫാണ്"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"മീഡിയ ഉപകരണം"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"അടിയന്തിര കോളുകൾ മാത്രം"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 0b1a1ff35120..8845b0722292 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Оруулах арга"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Байршил"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Байршил идэвхгүй"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Медиа төхөөрөмж"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Зөвхөн яаралтай дуудлага"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 9ed6bb345c6d..48498b7ae82d 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"इनपुट पद्धत"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"स्थान"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"स्थान बंद"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"मीडिया डिव्हाइस"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"फक्त आणीबाणीचे कॉल"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 81c15b066290..42044175c9e3 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Kaedah Input"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasi"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Lokasi Dimatikan"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Peranti media"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Panggilan Kecemasan Sahaja"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 6bccb33550ad..9b7488e1efd3 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"ထည့်သွင်းရန်နည်းလမ်း"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"တည်နေရာ"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"တည်နေရာပြမှု မရှိ"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"မီဒီယာ စက်ပစ္စည်း"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"အရေးပေါ်ခေါ်ဆိုမှုများသာ"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 90defaa3561d..490ba99e540e 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Inndatametode"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Sted"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Posisjon av"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medieenhet"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Bare nødanrop"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 0e86b94d58de..cf267fd98ee9 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"आगत विधि"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"स्थान"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"स्थान बन्द छ"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"मिडिया उपकरण"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"आपत्कालीन कल मात्र"</string> diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index 672d2f61e18b..c4cf440d4961 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -73,7 +73,7 @@ <color name="media_divider">#85ffffff</color> <!-- Biometric dialog colors --> - <color name="biometric_dialog_gray">#ff888888</color> + <color name="biometric_dialog_gray">#ffcccccc</color> <color name="biometric_dialog_accent">#ff80cbc4</color> <!-- light teal --> <color name="biometric_dialog_error">#fff28b82</color> <!-- red 300 --> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index a216c499844c..ddb9abc35b7e 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Invoermethode"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Locatie"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Locatie uit"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media-apparaat"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Alleen noodoproepen"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index ed38d141cb04..ce6723e80965 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"ଇନପୁଟ୍ ପଦ୍ଧତି"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ଲୋକେସନ୍"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"ଲୋକେସନ୍ ଅଫ୍"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"ମିଡିଆ ଡିଭାଇସ୍"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"କେବଳ ଜରୁରୀକାଳୀନ କଲ୍"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 3026a18cdde5..ad40d6ca8fac 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"ਇਨਪੁੱਟ ਵਿਧੀ"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ਟਿਕਾਣਾ"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਬੰਦ"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"ਮੀਡੀਆ ਡੀਵਾਈਸ"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"ਸਿਰਫ਼ ਸੰਕਟਕਾਲੀਨ ਕਾਲਾਂ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 12422e9851d7..75abfdace58e 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -346,6 +346,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Metoda wprowadzania"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokalizacja"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Lokalizacja wyłączona"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Urządzenie multimedialne"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Tylko połączenia alarmowe"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 0d65c0130c6e..1ab1002cbba4 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Método de entrada"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Localização desativada"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo de mídia"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Chamadas de emergência"</string> @@ -354,12 +358,9 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo usuário"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> - <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) --> - <skip /> - <!-- no translation found for quick_settings_networks_available (1875138606855420438) --> - <skip /> - <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) --> - <skip /> + <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Segura para aviões"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"Redes disponíveis"</string> + <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Redes indisponíveis"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Não conectado"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sem rede"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desligado"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 0932a96819a8..06b67fb4b367 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Método de Introdução"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Localização Desativada"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo multimédia"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Apenas chamadas de emergência"</string> @@ -354,12 +358,9 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo utilizador"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> - <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) --> - <skip /> - <!-- no translation found for quick_settings_networks_available (1875138606855420438) --> - <skip /> - <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) --> - <skip /> + <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Seguras para aviões"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"Redes disponíveis"</string> + <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Redes indisponíveis"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Não Ligado"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sem Rede"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Desligado"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 0d65c0130c6e..1ab1002cbba4 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Método de entrada"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Localização desativada"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo de mídia"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Chamadas de emergência"</string> @@ -354,12 +358,9 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo usuário"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> - <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) --> - <skip /> - <!-- no translation found for quick_settings_networks_available (1875138606855420438) --> - <skip /> - <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) --> - <skip /> + <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Segura para aviões"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"Redes disponíveis"</string> + <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Redes indisponíveis"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Não conectado"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sem rede"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desligado"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index e1819451aba5..54d53af19835 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -345,6 +345,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Metodă de introducere"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Locație"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Localizarea este dezactivată"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispozitiv media"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Numai apeluri de urgență"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 5d08e3a79f62..b6c2812c81ba 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -346,6 +346,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Способ ввода"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Геолокация"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Местоположение выкл."</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Режим медиа"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Экстр. вызов"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 7230a06f5eba..c1ba12bcda76 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"ආදාන ක්රමය"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ස්ථානය"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"ස්ථානය අක්රියයි"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"මාධ්ය උපාංගය"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"හදිසි ඇමතුම් පමණි"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 1454884e14da..dfa371ba15d5 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -346,6 +346,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Metóda vstupu"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Poloha"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Poloha vypnutá"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Mediálne zariadenie"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Len tiesňové volania"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 2cecff72906a..5f5ee6526480 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -346,6 +346,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Način vnosa"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Lokacija izklopljena"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Predstavnostna naprava"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Le klici v sili"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 3179e99e9b8f..e1ca27471098 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Metoda e hyrjes"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Vendndodhja"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Vendndodhja është e çaktivizuar"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Pajisje e jashtme ruajtëse"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Vetëm telefonata urgjence"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index ea862bc6061c..d7bc5a1b1776 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -345,6 +345,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Метод уноса"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Локација"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Локација је искључена"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Медијски уређај"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Само хитни позиви"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 66685e9dda2b..3e38bdd4d93d 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Inmatningsmetod"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Plats"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Plats har inaktiverats"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medieenhet"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Endast nödsamtal"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 1563c768ef16..5d1698dea7bb 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Mbinu ya uingizaji"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Kutambua Mahali"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Kitambua eneo kimezimwa"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Kifaa cha faili"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Simu za Dharura Pekee"</string> @@ -354,12 +358,9 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Mtumiaji mpya"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"Intaneti"</string> - <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) --> - <skip /> - <!-- no translation found for quick_settings_networks_available (1875138606855420438) --> - <skip /> - <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) --> - <skip /> + <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Hali salama ya ndegeni"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"Mitandao inapatikana"</string> + <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Mitandao haipatikani"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Haijaunganishwa"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Hakuna Mtandao"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Imezimwa"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index a54b1a26d75c..ebb7fa64aca5 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"உள்ளீட்டு முறை"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"இருப்பிடம்"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"இருப்பிடத்தை முடக்கு"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"மீடியா சாதனம்"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"அவசரகால அழைப்புகள் மட்டும்"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 01c3d7d15549..1456a8c586c1 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"ఇన్పుట్ పద్ధతి"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"లొకేషన్"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"లొకేషన్ ఆఫ్లో ఉంది"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"ప్రసార మాధ్యమ పరికరం"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"ఎమర్జెన్సీ కాల్స్ మాత్రమే"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index e51c3082d095..65ee10645ed2 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"วิธีป้อนข้อมูล"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ตำแหน่ง"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"ปิดตำแหน่ง"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"อุปกรณ์สื่อ"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"โทรฉุกเฉินเท่านั้น"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 7b8070b2e102..9eab088946a7 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Pamamaraan ng Pag-input"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasyon"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Naka-off ang Lokasyon"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Device ng media"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Mga Pang-emergency na Tawag Lamang"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index f9e78c7bcdac..921cce839d3d 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Giriş Yöntemi"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Konum"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Konum Bilgisi Kapalı"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medya cihazı"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Yalnızca Acil Çağrılar İçin"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 72046e1c2eb7..66a8cfb0157c 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -346,6 +346,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Метод введення"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Місцезнаходження"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Місцезнаходження вимкнено"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Носій"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Екстрені виклики"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 9ccc133093f1..3865bc04e959 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"ان پٹ کا طریقہ"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"مقام"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"مقام آف"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"میڈیا آلہ"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"صرف ہنگامی کالیں"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 67d801b4096e..d6061a6fd47c 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Kiritish usuli"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Joylashuv"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Joylashuvni aniqlash xizmati yoqilmagan"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media qurilma"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Favqulodda chaqiruvlar"</string> @@ -354,12 +358,9 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Yangi foydalanuvchi"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> - <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) --> - <skip /> - <!-- no translation found for quick_settings_networks_available (1875138606855420438) --> - <skip /> - <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) --> - <skip /> + <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Samolyot uchun xavfsiz"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"Tarmoqlar mavjud"</string> + <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Tarmoqqa ulanish imkonsiz"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ulanmagan"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tarmoq mavjud emas"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi o‘chiq"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 1d836e897f60..23e72c15a0bb 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Phương thức nhập"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Vị trí"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Tắt vị trí"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Thiết bị phương tiện"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Chỉ cuộc gọi khẩn cấp"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index fa6a5109fbc5..55b524ea4cfc 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"输入法"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"位置信息"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"位置信息:关闭"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"媒体设备"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"只能拨打紧急呼救电话"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index b69d09796aae..701f32dee855 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"輸入法"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"位置"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"位置資訊已關閉"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"媒體裝置"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"只可撥打緊急電話"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 2fb0b865625f..4210ec1cf9ea 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"輸入法"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"定位"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"定位服務已關閉"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"媒體裝置"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"僅可撥打緊急電話"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index e9e1a75c7fa4..940fc19d13ad 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -344,6 +344,10 @@ <string name="quick_settings_ime_label" msgid="3351174938144332051">"Indlela yokungenayo"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Indawo"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Indawo ivaliwe"</string> + <!-- no translation found for quick_settings_camera_label (1367149596242401934) --> + <skip /> + <!-- no translation found for quick_settings_mic_label (8245831073612564953) --> + <skip /> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Idivayisi yemidiya"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Amakholi aphuthumayo kuphela"</string> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 101124ec2a2e..93d2f751e754 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -107,7 +107,7 @@ <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" --> <string name="quick_settings_tiles_stock" translatable="false"> - wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness + wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle,mictoggle </string> <!-- The tiles to display in QuickSettings --> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 72dd72410af3..d104d17f37b7 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -619,7 +619,7 @@ <dimen name="z_distance_between_notifications">0.5dp</dimen> <!-- The height of the divider between the individual notifications. --> - <dimen name="notification_divider_height">4dp</dimen> + <dimen name="notification_divider_height">2dp</dimen> <!-- The corner radius of the shadow behind the notification. --> <dimen name="notification_shadow_radius">0dp</dimen> @@ -1180,10 +1180,9 @@ <dimen name="new_qs_vertical_margin">8dp</dimen> <!-- Size of media cards in the QSPanel carousel --> - <dimen name="qs_media_width">350dp</dimen> <dimen name="qs_media_padding">16dp</dimen> <dimen name="qs_media_panel_outer_padding">16dp</dimen> - <dimen name="qs_media_album_size">52dp</dimen> + <dimen name="qs_media_album_size">120dp</dimen> <dimen name="qs_media_icon_size">16dp</dimen> <dimen name="qs_center_guideline_padding">10dp</dimen> <dimen name="qs_seamless_icon_size">@dimen/qs_media_icon_size</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 7cdd8b1b80ca..ac2e342b3c34 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -821,6 +821,10 @@ <string name="quick_settings_location_label">Location</string> <!-- QuickSettings: Location (Off) [CHAR LIMIT=NONE] --> <string name="quick_settings_location_off_label">Location Off</string> + <!-- QuickSettings: Camera [CHAR LIMIT=NONE] --> + <string name="quick_settings_camera_label">Block Camera</string> + <!-- QuickSettings: Microphone [CHAR LIMIT=NONE] --> + <string name="quick_settings_mic_label">Mute Microphone</string> <!-- QuickSettings: Media device [CHAR LIMIT=NONE] --> <string name="quick_settings_media_device_label">Media device</string> <!-- QuickSettings: RSSI [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/res/xml/media_collapsed.xml b/packages/SystemUI/res/xml/media_collapsed.xml index ee958f28c51b..f834d6df15c2 100644 --- a/packages/SystemUI/res/xml/media_collapsed.xml +++ b/packages/SystemUI/res/xml/media_collapsed.xml @@ -22,36 +22,39 @@ android:layout_width="@dimen/qs_media_icon_size" android:layout_height="@dimen/qs_media_icon_size" android:layout_marginStart="18dp" - app:layout_constraintTop_toTopOf="@id/app_name" - app:layout_constraintBottom_toBottomOf="@id/app_name" - app:layout_constraintStart_toStartOf="parent" + android:layout_marginTop="@dimen/qs_media_panel_outer_padding" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toEndOf="@id/album_art" /> <Constraint android:id="@+id/app_name" - android:layout_width="0dp" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/qs_center_guideline_padding" - android:layout_marginStart="10dp" - android:layout_marginTop="20dp" - app:layout_constraintTop_toTopOf="parent" + android:layout_marginStart="8dp" + app:layout_constraintTop_toTopOf="@id/icon" + app:layout_constraintBottom_toBottomOf="@id/icon" app:layout_constraintStart_toEndOf="@id/icon" - app:layout_constraintEnd_toStartOf="@id/center_vertical_guideline" + app:layout_constraintEnd_toStartOf="@id/media_seamless" + app:layout_constraintHorizontal_chainStyle="spread_inside" + app:layout_constrainedWidth="true" app:layout_constraintHorizontal_bias="0" /> <Constraint android:id="@+id/media_seamless" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" - app:layout_constrainedWidth="true" - app:layout_constraintWidth_min="60dp" - app:layout_constraintStart_toEndOf="@id/center_vertical_guideline" + app:layout_constraintStart_toEndOf="@id/app_name" + app:layout_constraintHorizontal_chainStyle="spread_inside" app:layout_constraintHorizontal_bias="1" - android:layout_marginTop="@dimen/qs_media_panel_outer_padding" - android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + app:layout_constrainedWidth="true" + app:layout_constraintWidth_min="48dp" + app:layout_constraintHeight_min="48dp" + android:layout_marginEnd="@dimen/qs_center_guideline_padding" android:layout_marginStart="@dimen/qs_center_guideline_padding" /> @@ -64,22 +67,23 @@ android:alpha="0.5" android:visibility="gone" app:layout_constraintHorizontal_bias="1" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="@id/app_name" - app:layout_constraintBottom_toBottomOf="@id/app_name" + app:layout_constraintTop_toTopOf="@id/icon" + app:layout_constraintBottom_toBottomOf="@id/icon" app:layout_constraintStart_toEndOf="@id/center_vertical_guideline" + app:layout_constraintEnd_toEndOf="parent" /> <Constraint android:id="@+id/album_art" android:layout_width="@dimen/qs_media_album_size" android:layout_height="@dimen/qs_media_album_size" - android:layout_marginTop="16dp" + android:layout_marginTop="@dimen/qs_media_panel_outer_padding" android:layout_marginStart="@dimen/qs_media_panel_outer_padding" - android:layout_marginBottom="24dp" - app:layout_constraintTop_toBottomOf="@id/icon" + android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" + app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@id/media_action_barrier" + app:layout_constraintHorizontal_bias="0" /> <!-- Song name --> @@ -87,13 +91,14 @@ android:id="@+id/header_title" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="17dp" - android:layout_marginStart="16dp" + android:layout_marginTop="@dimen/qqs_media_spacing" + android:layout_marginStart="@dimen/qqs_media_spacing" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" app:layout_constrainedWidth="true" - app:layout_constraintTop_toBottomOf="@id/app_name" + app:layout_constraintTop_toBottomOf="@id/icon" app:layout_constraintBottom_toTopOf="@id/header_artist" app:layout_constraintStart_toEndOf="@id/album_art" - app:layout_constraintEnd_toStartOf="@id/media_action_barrier" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0"/> <!-- Artist name --> @@ -102,12 +107,12 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="3dp" - android:layout_marginBottom="24dp" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + android:layout_marginBottom="@dimen/qqs_media_spacing" app:layout_constrainedWidth="true" app:layout_constraintTop_toBottomOf="@id/header_title" app:layout_constraintStart_toStartOf="@id/header_title" - app:layout_constraintEnd_toStartOf="@id/media_action_barrier" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0"/> <!-- Seek Bar --> @@ -140,15 +145,15 @@ android:id="@+id/action0" android:layout_width="48dp" android:layout_height="48dp" - android:layout_marginStart="4dp" + android:layout_marginStart="@dimen/qqs_media_spacing" android:layout_marginEnd="4dp" - android:layout_marginTop="18dp" + android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" android:visibility="gone" app:layout_constraintHorizontal_chainStyle="packed" - app:layout_constraintTop_toBottomOf="@id/app_name" + app:layout_constraintTop_toBottomOf="@id/header_artist" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toLeftOf="@id/action1" - app:layout_constraintHorizontal_bias="1" + app:layout_constraintHorizontal_bias="0" > </Constraint> @@ -158,8 +163,9 @@ android:layout_height="48dp" android:layout_marginStart="4dp" android:layout_marginEnd="4dp" - android:layout_marginTop="18dp" - app:layout_constraintTop_toBottomOf="@id/app_name" + android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" + app:layout_constraintTop_toBottomOf="@id/header_artist" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toRightOf="@id/action0" app:layout_constraintRight_toLeftOf="@id/action2" > @@ -171,8 +177,9 @@ android:layout_height="48dp" android:layout_marginStart="4dp" android:layout_marginEnd="4dp" - android:layout_marginTop="18dp" - app:layout_constraintTop_toBottomOf="@id/app_name" + android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" + app:layout_constraintTop_toBottomOf="@id/header_artist" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toRightOf="@id/action1" app:layout_constraintRight_toLeftOf="@id/action3" > @@ -184,8 +191,9 @@ android:layout_height="48dp" android:layout_marginStart="4dp" android:layout_marginEnd="4dp" - android:layout_marginTop="18dp" - app:layout_constraintTop_toBottomOf="@id/app_name" + android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" + app:layout_constraintTop_toBottomOf="@id/header_artist" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toRightOf="@id/action2" app:layout_constraintRight_toLeftOf="@id/action4" > @@ -196,11 +204,12 @@ android:layout_width="48dp" android:layout_height="48dp" android:layout_marginStart="4dp" - android:layout_marginEnd="4dp" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" android:visibility="gone" - android:layout_marginTop="18dp" + android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" app:layout_constraintHorizontal_chainStyle="packed" - app:layout_constraintTop_toBottomOf="@id/app_name" + app:layout_constraintTop_toBottomOf="@id/header_artist" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toRightOf="@id/action3" app:layout_constraintRight_toRightOf="parent" app:layout_constraintHorizontal_bias="0" diff --git a/packages/SystemUI/res/xml/media_expanded.xml b/packages/SystemUI/res/xml/media_expanded.xml index d5a02c2d39d5..d89e0eb4df63 100644 --- a/packages/SystemUI/res/xml/media_expanded.xml +++ b/packages/SystemUI/res/xml/media_expanded.xml @@ -22,36 +22,39 @@ android:layout_width="@dimen/qs_media_icon_size" android:layout_height="@dimen/qs_media_icon_size" android:layout_marginStart="18dp" - app:layout_constraintTop_toTopOf="@id/app_name" - app:layout_constraintBottom_toBottomOf="@id/app_name" - app:layout_constraintStart_toStartOf="parent" + android:layout_marginTop="@dimen/qs_media_panel_outer_padding" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toEndOf="@id/album_art" /> <Constraint android:id="@+id/app_name" - android:layout_width="0dp" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/qs_center_guideline_padding" - android:layout_marginStart="10dp" - android:layout_marginTop="20dp" - app:layout_constraintTop_toTopOf="parent" + android:layout_marginStart="8dp" + app:layout_constraintTop_toTopOf="@id/icon" + app:layout_constraintBottom_toBottomOf="@id/icon" app:layout_constraintStart_toEndOf="@id/icon" - app:layout_constraintEnd_toStartOf="@id/center_vertical_guideline" + app:layout_constraintEnd_toStartOf="@id/media_seamless" + app:layout_constraintHorizontal_chainStyle="spread_inside" + app:layout_constrainedWidth="true" app:layout_constraintHorizontal_bias="0" /> <Constraint android:id="@+id/media_seamless" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintStart_toEndOf="@id/center_vertical_guideline" + app:layout_constraintStart_toEndOf="@id/app_name" + app:layout_constraintHorizontal_chainStyle="spread_inside" app:layout_constraintHorizontal_bias="1" app:layout_constrainedWidth="true" - app:layout_constraintWidth_min="60dp" - android:layout_marginTop="@dimen/qs_media_panel_outer_padding" - android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + app:layout_constraintWidth_min="48dp" + app:layout_constraintHeight_min="48dp" + android:layout_marginEnd="@dimen/qs_center_guideline_padding" android:layout_marginStart="@dimen/qs_center_guideline_padding" /> @@ -63,20 +66,21 @@ android:layout_marginStart="@dimen/qs_center_guideline_padding" android:alpha="0.5" android:visibility="gone" - app:layout_constraintTop_toTopOf="@id/app_name" - app:layout_constraintBottom_toBottomOf="@id/app_name" + app:layout_constraintHorizontal_bias="1" + app:layout_constraintTop_toTopOf="@id/icon" + app:layout_constraintBottom_toBottomOf="@id/icon" app:layout_constraintStart_toEndOf="@id/center_vertical_guideline" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="1" /> <Constraint android:id="@+id/album_art" android:layout_width="@dimen/qs_media_album_size" android:layout_height="@dimen/qs_media_album_size" - android:layout_marginTop="14dp" + android:layout_marginTop="@dimen/qs_media_panel_outer_padding" android:layout_marginStart="@dimen/qs_media_panel_outer_padding" - app:layout_constraintTop_toBottomOf="@+id/app_name" + android:layout_marginBottom="@dimen/qqs_media_spacing" + app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" /> @@ -85,11 +89,11 @@ android:id="@+id/header_title" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginTop="@dimen/qqs_media_spacing" + android:layout_marginStart="@dimen/qqs_media_spacing" android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" - android:layout_marginTop="17dp" - android:layout_marginStart="16dp" app:layout_constrainedWidth="true" - app:layout_constraintTop_toBottomOf="@+id/app_name" + app:layout_constraintTop_toBottomOf="@+id/icon" app:layout_constraintStart_toEndOf="@id/album_art" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0"/> @@ -100,6 +104,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + android:layout_marginBottom="@dimen/qqs_media_spacing" android:layout_marginTop="3dp" app:layout_constrainedWidth="true" app:layout_constraintTop_toBottomOf="@id/header_title" @@ -112,7 +117,7 @@ android:id="@+id/media_progress_bar" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginTop="3dp" + android:layout_marginTop="35dp" app:layout_constraintTop_toBottomOf="@id/header_artist" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" @@ -122,7 +127,7 @@ android:id="@+id/notification_media_progress_time" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginTop="38dp" + android:layout_marginTop="70dp" android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" android:layout_marginStart="@dimen/qs_media_panel_outer_padding" app:layout_constraintTop_toBottomOf="@id/header_artist" @@ -135,13 +140,12 @@ android:layout_width="48dp" android:layout_height="48dp" android:layout_marginTop="5dp" - android:layout_marginStart="4dp" - android:layout_marginEnd="4dp" + android:layout_marginStart="@dimen/qs_media_panel_outer_padding" android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toLeftOf="@id/action1" - app:layout_constraintTop_toBottomOf="@id/notification_media_progress_time" + app:layout_constraintTop_toBottomOf="@id/media_progress_bar" app:layout_constraintBottom_toBottomOf="parent"> </Constraint> @@ -155,6 +159,7 @@ app:layout_constraintLeft_toRightOf="@id/action0" app:layout_constraintRight_toLeftOf="@id/action2" app:layout_constraintTop_toTopOf="@id/action0" + app:layout_constraintTop_toBottomOf="@id/media_progress_bar" app:layout_constraintBottom_toBottomOf="parent"> </Constraint> @@ -167,7 +172,7 @@ android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" app:layout_constraintLeft_toRightOf="@id/action1" app:layout_constraintRight_toLeftOf="@id/action3" - app:layout_constraintTop_toTopOf="@id/action0" + app:layout_constraintTop_toBottomOf="@id/media_progress_bar" app:layout_constraintBottom_toBottomOf="parent"> </Constraint> @@ -177,10 +182,10 @@ android:layout_height="48dp" android:layout_marginStart="4dp" android:layout_marginEnd="4dp" + android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" app:layout_constraintLeft_toRightOf="@id/action2" app:layout_constraintRight_toLeftOf="@id/action4" - app:layout_constraintTop_toTopOf="@id/action0" - android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" + app:layout_constraintTop_toBottomOf="@id/media_progress_bar" app:layout_constraintBottom_toBottomOf="parent"> </Constraint> @@ -189,12 +194,12 @@ android:layout_width="48dp" android:layout_height="48dp" android:layout_marginStart="4dp" - android:layout_marginEnd="4dp" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintLeft_toRightOf="@id/action3" app:layout_constraintRight_toRightOf="parent" - app:layout_constraintTop_toTopOf="@id/action0" + app:layout_constraintTop_toBottomOf="@id/media_progress_bar" app:layout_constraintBottom_toBottomOf="parent"> </Constraint> </ConstraintSet> diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml index 6eec5dc9e1c1..902de23a9e2a 100644 --- a/packages/SystemUI/res/xml/tuner_prefs.xml +++ b/packages/SystemUI/res/xml/tuner_prefs.xml @@ -50,6 +50,10 @@ android:key="bluetooth" android:title="@string/quick_settings_bluetooth_label" /> + <com.android.systemui.tuner.StatusBarSwitch + android:key="cameratoggle" + android:title="@string/quick_settings_camera_label" /> + <!-- nfc --> <!-- tty --> <!-- speakerphone --> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index e4427f49e030..57a4dab5729c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -24,9 +24,11 @@ import android.graphics.Insets; import android.graphics.Rect; import android.os.Bundle; import android.view.MotionEvent; +import android.window.TransitionFilter; import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.system.RemoteTransitionCompat; /** * Temporary callbacks into SystemUI. @@ -192,4 +194,13 @@ interface ISystemUiProxy { * @param destinationBounds the destination bounds the PiP window lands into */ void stopSwipePipToHome(in ComponentName componentName, in Rect destinationBounds) = 31; + + /** + * Registers a RemoteTransitionCompat that will handle transitions. This parameter bundles an + * IRemoteTransition and a filter that must pass for it. + */ + void registerRemoteTransition(in RemoteTransitionCompat remoteTransition) = 32; + + /** Unegisters a RemoteTransitionCompat that will handle transitions. */ + void unregisterRemoteTransition(in RemoteTransitionCompat remoteTransition) = 33; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java index d672c2c1dc58..325e268a63ea 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java @@ -60,7 +60,8 @@ public abstract class ActivityOptionsCompat { public static ActivityOptions makeRemoteAnimation( RemoteAnimationAdapterCompat remoteAnimationAdapter) { - return ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter.getWrapped()); + return ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter.getWrapped(), + remoteAnimationAdapter.getRemoteTransition().getTransition()); } public static ActivityOptions makeCustomAnimation(Context context, int enterResId, diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java index 02e509eef25f..f105fcec8e9f 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java @@ -16,12 +16,21 @@ package com.android.systemui.shared.system; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_OPEN; +import static android.view.WindowManager.TRANSIT_TO_BACK; +import static android.view.WindowManager.TRANSIT_TO_FRONT; + import android.os.RemoteException; import android.util.Log; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; +import android.view.SurfaceControl; +import android.window.IRemoteTransition; +import android.window.TransitionInfo; /** * @see RemoteAnimationAdapter @@ -29,17 +38,28 @@ import android.view.RemoteAnimationTarget; public class RemoteAnimationAdapterCompat { private final RemoteAnimationAdapter mWrapped; + private final RemoteTransitionCompat mRemoteTransition; public RemoteAnimationAdapterCompat(RemoteAnimationRunnerCompat runner, long duration, long statusBarTransitionDelay) { mWrapped = new RemoteAnimationAdapter(wrapRemoteAnimationRunner(runner), duration, statusBarTransitionDelay); + mRemoteTransition = buildRemoteTransition(runner); } RemoteAnimationAdapter getWrapped() { return mWrapped; } + /** Helper to just build a remote transition. Use this if the legacy adapter isn't needed. */ + public static RemoteTransitionCompat buildRemoteTransition(RemoteAnimationRunnerCompat runner) { + return new RemoteTransitionCompat(wrapRemoteTransition(runner)); + } + + public RemoteTransitionCompat getRemoteTransition() { + return mRemoteTransition; + } + private static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner( final RemoteAnimationRunnerCompat remoteAnimationAdapter) { return new IRemoteAnimationRunner.Stub() { @@ -72,4 +92,63 @@ public class RemoteAnimationAdapterCompat { } }; } + + private static IRemoteTransition.Stub wrapRemoteTransition( + final RemoteAnimationRunnerCompat remoteAnimationAdapter) { + return new IRemoteTransition.Stub() { + @Override + public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t, + IRemoteAnimationFinishedCallback finishCallback) { + final RemoteAnimationTargetCompat[] appsCompat = + RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */); + final RemoteAnimationTargetCompat[] wallpapersCompat = + RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */); + final Runnable animationFinishedCallback = new Runnable() { + @Override + public void run() { + try { + finishCallback.onAnimationFinished(); + } catch (RemoteException e) { + Log.e("ActivityOptionsCompat", "Failed to call app controlled animation" + + " finished callback", e); + } + } + }; + + // TODO(b/177438007): Move this set-up logic into launcher's animation impl. + boolean isReturnToHome = false; + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + if (change.getTaskInfo() != null + && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME) { + isReturnToHome = change.getMode() == TRANSIT_OPEN + || change.getMode() == TRANSIT_TO_FRONT; + break; + } + } + + if (isReturnToHome) { + // Need to "boost" the closing things since that's what launcher expects. + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + final SurfaceControl leash = change.getLeash(); + final int mode = info.getChanges().get(i).getMode(); + // Only deal with roots + if (change.getParent() != null) continue; + if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) { + t.setLayer(leash, info.getChanges().size() * 3 - i); + } + } + // Make wallpaper visible immediately since launcher apparently won't do this. + for (int i = wallpapersCompat.length - 1; i >= 0; --i) { + t.show(wallpapersCompat[i].leash.getSurfaceControl()); + t.setAlpha(wallpapersCompat[i].leash.getSurfaceControl(), 1.f); + } + } + t.apply(); + remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat, + animationFinishedCallback); + } + }; + } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java index a1c1f93638a8..f88e38a5923c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java @@ -22,6 +22,10 @@ import android.graphics.Point; import android.graphics.Rect; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; +import android.view.WindowManager; +import android.window.TransitionInfo; + +import java.util.ArrayList; /** * @see RemoteAnimationTarget @@ -73,6 +77,45 @@ public class RemoteAnimationTargetCompat { mStartLeash = app.startLeash; } + private static int newModeToLegacyMode(int newMode) { + switch (newMode) { + case WindowManager.TRANSIT_OPEN: + case WindowManager.TRANSIT_TO_FRONT: + return MODE_OPENING; + case WindowManager.TRANSIT_CLOSE: + case WindowManager.TRANSIT_TO_BACK: + return MODE_CLOSING; + default: + return 2; // MODE_CHANGING + } + } + + public RemoteAnimationTargetCompat(TransitionInfo.Change change, int order) { + taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1; + mode = newModeToLegacyMode(change.getMode()); + leash = new SurfaceControlCompat(change.getLeash()); + isTranslucent = (change.getFlags() & TransitionInfo.FLAG_TRANSLUCENT) != 0 + || (change.getFlags() & TransitionInfo.FLAG_SHOW_WALLPAPER) != 0; + clipRect = null; + position = null; + localBounds = new Rect(change.getEndAbsBounds()); + localBounds.offsetTo(change.getEndRelOffset().x, change.getEndRelOffset().y); + sourceContainerBounds = null; + screenSpaceBounds = change.getEndAbsBounds(); + prefixOrderIndex = order; + // TODO(shell-transitions): I guess we need to send content insets? evaluate how its used. + contentInsets = new Rect(0, 0, 0, 0); + if (change.getTaskInfo() != null) { + isNotInRecents = !change.getTaskInfo().isRunning; + activityType = change.getTaskInfo().getActivityType(); + } else { + isNotInRecents = true; + activityType = ACTIVITY_TYPE_UNDEFINED; + } + pictureInPictureParams = null; + mStartLeash = null; + } + public static RemoteAnimationTargetCompat[] wrap(RemoteAnimationTarget[] apps) { final RemoteAnimationTargetCompat[] appsCompat = new RemoteAnimationTargetCompat[apps != null ? apps.length : 0]; @@ -83,6 +126,24 @@ public class RemoteAnimationTargetCompat { } /** + * Represents a TransitionInfo object as an array of old-style targets + * + * @param wallpapers If true, this will return wallpaper targets; otherwise it returns + * non-wallpaper targets. + */ + public static RemoteAnimationTargetCompat[] wrap(TransitionInfo info, boolean wallpapers) { + final ArrayList<RemoteAnimationTargetCompat> out = new ArrayList<>(); + for (int i = 0; i < info.getChanges().size(); i++) { + boolean changeIsWallpaper = + (info.getChanges().get(i).getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0; + if (wallpapers != changeIsWallpaper) continue; + out.add(new RemoteAnimationTargetCompat(info.getChanges().get(i), + info.getChanges().size() - i)); + } + return out.toArray(new RemoteAnimationTargetCompat[out.size()]); + } + + /** * @see SurfaceControl#release() */ public void release() { @@ -91,4 +152,4 @@ public class RemoteAnimationTargetCompat { mStartLeash.release(); } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.aidl new file mode 100644 index 000000000000..1550ab3bed63 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.system; + +parcelable RemoteTransitionCompat; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java new file mode 100644 index 000000000000..5c27b89c03de --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.system; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.view.WindowManager.TRANSIT_OPEN; +import static android.view.WindowManager.TRANSIT_TO_FRONT; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcelable; +import android.window.IRemoteTransition; +import android.window.TransitionFilter; + +import com.android.internal.util.DataClass; + +/** + * Wrapper to expose RemoteTransition (shell transitions) to Launcher. + * + * @see IRemoteTransition + * @see TransitionFilter + */ +@DataClass +public class RemoteTransitionCompat implements Parcelable { + @NonNull final IRemoteTransition mTransition; + @Nullable TransitionFilter mFilter = null; + + RemoteTransitionCompat(IRemoteTransition transition) { + mTransition = transition; + } + + /** Adds a filter check that restricts this remote transition to home open transitions. */ + public void addHomeOpenCheck() { + if (mFilter == null) { + mFilter = new TransitionFilter(); + } + mFilter.mRequirements = + new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()}; + mFilter.mRequirements[0].mActivityType = ACTIVITY_TYPE_HOME; + mFilter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; + } + + + + // Code below generated by codegen v1.0.21. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + /* package-private */ RemoteTransitionCompat( + @NonNull IRemoteTransition transition, + @Nullable TransitionFilter filter) { + this.mTransition = transition; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTransition); + this.mFilter = filter; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public @NonNull IRemoteTransition getTransition() { + return mTransition; + } + + @DataClass.Generated.Member + public @Nullable TransitionFilter getFilter() { + return mFilter; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mFilter != null) flg |= 0x2; + dest.writeByte(flg); + dest.writeStrongInterface(mTransition); + if (mFilter != null) dest.writeTypedObject(mFilter, flags); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected RemoteTransitionCompat(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + IRemoteTransition transition = IRemoteTransition.Stub.asInterface(in.readStrongBinder()); + TransitionFilter filter = (flg & 0x2) == 0 ? null : (TransitionFilter) in.readTypedObject(TransitionFilter.CREATOR); + + this.mTransition = transition; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTransition); + this.mFilter = filter; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<RemoteTransitionCompat> CREATOR + = new Parcelable.Creator<RemoteTransitionCompat>() { + @Override + public RemoteTransitionCompat[] newArray(int size) { + return new RemoteTransitionCompat[size]; + } + + @Override + public RemoteTransitionCompat createFromParcel(@NonNull android.os.Parcel in) { + return new RemoteTransitionCompat(in); + } + }; + + /** + * A builder for {@link RemoteTransitionCompat} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static class Builder { + + private @NonNull IRemoteTransition mTransition; + private @Nullable TransitionFilter mFilter; + + private long mBuilderFieldsSet = 0L; + + public Builder( + @NonNull IRemoteTransition transition) { + mTransition = transition; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTransition); + } + + @DataClass.Generated.Member + public @NonNull Builder setTransition(@NonNull IRemoteTransition value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mTransition = value; + return this; + } + + @DataClass.Generated.Member + public @NonNull Builder setFilter(@NonNull TransitionFilter value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mFilter = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull RemoteTransitionCompat build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; // Mark builder used + + if ((mBuilderFieldsSet & 0x2) == 0) { + mFilter = null; + } + RemoteTransitionCompat o = new RemoteTransitionCompat( + mTransition, + mFilter); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x4) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1606862689344L, + codegenVersion = "1.0.21", + sourceFile = "frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java", + inputSignatures = "final @android.annotation.NonNull com.android.systemui.shared.system.IRemoteTransition mTransition\n @android.annotation.Nullable android.window.TransitionFilter mFilter\npublic void addHomeOpenCheck()\nclass RemoteTransitionCompat extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index deca14a3cad3..19520df08757 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -30,6 +30,7 @@ import com.android.systemui.dagger.SysUIComponent; import com.android.systemui.dagger.WMComponent; import com.android.systemui.navigationbar.gestural.BackGestureTfClassifierProvider; import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider; +import com.android.wm.shell.transition.Transitions; import java.util.Optional; import java.util.concurrent.ExecutionException; @@ -113,7 +114,8 @@ public class SystemUIFactory { .setHideDisplayCutout(mWMComponent.getHideDisplayCutout()) .setShellCommandHandler(mWMComponent.getShellCommandHandler()) .setAppPairs(mWMComponent.getAppPairs()) - .setTaskViewFactory(mWMComponent.getTaskViewFactory()); + .setTaskViewFactory(mWMComponent.getTaskViewFactory()) + .setTransitions(mWMComponent.getTransitions()); } else { // TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option // is separating this logic into newly creating SystemUITestsFactory. @@ -126,7 +128,8 @@ public class SystemUIFactory { .setHideDisplayCutout(Optional.ofNullable(null)) .setShellCommandHandler(Optional.ofNullable(null)) .setAppPairs(Optional.ofNullable(null)) - .setTaskViewFactory(Optional.ofNullable(null)); + .setTaskViewFactory(Optional.ofNullable(null)) + .setTransitions(Transitions.createEmptyForTesting()); } mSysUIComponent = builder.build(); if (initializeComponents) { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java index e6ad1cb9ea88..13da2b06c976 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java @@ -16,7 +16,7 @@ package com.android.systemui.accessibility; -import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS; +import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_ACCESSIBILITY_ACTIONS; import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; @@ -349,7 +349,7 @@ public class SystemActions extends SystemUI { private void handleTakeScreenshot() { ScreenshotHelper screenshotHelper = new ScreenshotHelper(mContext); screenshotHelper.takeScreenshot(WindowManager.TAKE_SCREENSHOT_FULLSCREEN, true, true, - SCREENSHOT_GLOBAL_ACTIONS, new Handler(Looper.getMainLooper()), null); + SCREENSHOT_ACCESSIBILITY_ACTIONS, new Handler(Looper.getMainLooper()), null); } private void handleAccessibilityButton() { diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java index 61951cc0b5e9..169a9c0c6eac 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java @@ -112,7 +112,7 @@ public final class PhoneStateMonitor { } else if (mLauncherShowing) { phoneState = getPhoneLauncherState(); } else { - phoneState = getPhoneAppState(); + phoneState = PHONE_STATE_APP_IMMERSIVE; } return phoneState; } @@ -161,16 +161,6 @@ public final class PhoneStateMonitor { } } - private int getPhoneAppState() { - if (isAppImmersive()) { - return PHONE_STATE_APP_IMMERSIVE; - } else if (isAppFullscreen()) { - return PHONE_STATE_APP_FULLSCREEN; - } else { - return PHONE_STATE_APP_DEFAULT; - } - } - private boolean isShadeFullscreen() { int statusBarState = mStatusBarStateController.getState(); return statusBarState == StatusBarState.KEYGUARD @@ -189,14 +179,6 @@ public final class PhoneStateMonitor { } } - private boolean isAppImmersive() { - return mStatusBarOptionalLazy.get().get().inImmersiveMode(); - } - - private boolean isAppFullscreen() { - return mStatusBarOptionalLazy.get().get().inFullscreenMode(); - } - private boolean isBouncerShowing() { return mStatusBarOptionalLazy.map( statusBarLazy -> statusBarLazy.get().isBouncerShowing()).orElse(false); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java index e4f6d6cc6887..9b09cfd0dba6 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java @@ -160,12 +160,12 @@ public class AuthBiometricFaceView extends AuthBiometricView { @Override protected void handleResetAfterError() { - resetErrorView(mContext, mIndicatorView); + resetErrorView(); } @Override protected void handleResetAfterHelp() { - resetErrorView(mContext, mIndicatorView); + resetErrorView(); } @Override @@ -185,7 +185,7 @@ public class AuthBiometricFaceView extends AuthBiometricView { if (newState == STATE_AUTHENTICATING_ANIMATING_IN || (newState == STATE_AUTHENTICATING && getSize() == AuthDialog.SIZE_MEDIUM)) { - resetErrorView(mContext, mIndicatorView); + resetErrorView(); } // Do this last since the state variable gets updated. @@ -204,9 +204,8 @@ public class AuthBiometricFaceView extends AuthBiometricView { super.onAuthenticationFailed(failureReason); } - static void resetErrorView(Context context, TextView textView) { - textView.setTextColor(context.getResources().getColor( - R.color.biometric_dialog_gray, context.getTheme())); - textView.setVisibility(View.INVISIBLE); + private void resetErrorView() { + mIndicatorView.setTextColor(mTextColorHint); + mIndicatorView.setVisibility(View.INVISIBLE); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java index 176e9e6b1c9b..45ee4ad9ae50 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java @@ -78,7 +78,7 @@ public class AuthBiometricFingerprintView extends AuthBiometricView { private void showTouchSensorString() { mIndicatorView.setText(R.string.fingerprint_dialog_touch_sensor); - mIndicatorView.setTextColor(R.color.biometric_dialog_gray); + mIndicatorView.setTextColor(mTextColorHint); } private void updateIcon(int lastState, int newState) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java index c748ab21b822..18206efefe9a 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java @@ -83,7 +83,7 @@ public abstract class AuthBiometricView extends LinearLayout { * Authenticated, dialog animating away soon. */ protected static final int STATE_AUTHENTICATED = 6; - + @Retention(RetentionPolicy.SOURCE) @IntDef({STATE_IDLE, STATE_AUTHENTICATING_ANIMATING_IN, STATE_AUTHENTICATING, STATE_HELP, STATE_ERROR, STATE_PENDING_CONFIRMATION, STATE_AUTHENTICATED}) @@ -168,8 +168,8 @@ public abstract class AuthBiometricView extends LinearLayout { private final Injector mInjector; private final Handler mHandler; private final AccessibilityManager mAccessibilityManager; - private final int mTextColorError; - private final int mTextColorHint; + protected final int mTextColorError; + protected final int mTextColorHint; private AuthPanelController mPanelController; private PromptInfo mPromptInfo; @@ -183,7 +183,7 @@ public abstract class AuthBiometricView extends LinearLayout { private TextView mDescriptionView; private View mIconHolderView; protected ImageView mIconView; - @VisibleForTesting protected TextView mIndicatorView; + protected TextView mIndicatorView; // Negative button position, exclusively for the app-specified behavior @VisibleForTesting Button mNegativeButton; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index dcb6ea3ef5b7..1cafb4c2c313 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -342,8 +342,8 @@ class UdfpsController implements DozeReceiver { Log.v(TAG, "showUdfpsOverlay | adding window"); mView.setShowReason(reason); mWindowManager.addView(mView, computeLayoutParams()); - mIsOverlayShowing = true; mView.setOnTouchListener(mOnTouchListener); + mIsOverlayShowing = true; } catch (RuntimeException e) { Log.e(TAG, "showUdfpsOverlay | failed to add window", e); } @@ -434,19 +434,21 @@ class UdfpsController implements DozeReceiver { } private void onFingerDown(int x, int y, float minor, float major) { - mView.setScrimAlpha(computeScrimOpacity()); - mView.showScrimAndDot(); - try { - if (mHbmSupported) { + if (mHbmSupported) { + try { FileWriter fw = new FileWriter(mHbmPath); fw.write(mHbmEnableCommand); fw.close(); + } catch (IOException e) { + mView.hideScrimAndDot(); + Log.e(TAG, "onFingerDown | failed to enable HBM: " + e.getMessage()); } - mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major); - } catch (IOException e) { - mView.hideScrimAndDot(); - Log.e(TAG, "onFingerDown | failed to enable HBM: " + e.getMessage()); } + mView.setScrimAlpha(computeScrimOpacity()); + mView.setRunAfterShowingScrimAndDot(() -> { + mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major); + }); + mView.showScrimAndDot(); } private void onFingerUp() { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java index a42ab58487b9..c2f80d4d2a5b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java @@ -89,6 +89,10 @@ public class UdfpsView extends View implements DozeReceiver, private boolean mIsHbmSupported; @Nullable private String mDebugMessage; + // Runnable that will be run after the illumination dot and scrim are shown. + // The runnable is reset to null after it's executed once. + @Nullable private Runnable mRunAfterShowingScrimAndDot; + public UdfpsView(Context context, AttributeSet attrs) { super(context, attrs); @@ -279,6 +283,11 @@ public class UdfpsView extends View implements DozeReceiver, } canvas.restore(); + + if (mShowScrimAndDot && mRunAfterShowingScrimAndDot != null) { + post(mRunAfterShowingScrimAndDot); + mRunAfterShowingScrimAndDot = null; + } } RectF getSensorRect() { @@ -294,6 +303,10 @@ public class UdfpsView extends View implements DozeReceiver, postInvalidate(); } + void setRunAfterShowingScrimAndDot(Runnable runnable) { + mRunAfterShowingScrimAndDot = runnable; + } + boolean isValidTouch(float x, float y, float pressure) { // The X and Y coordinates of the sensor's center. final float cx = mSensorRect.centerX(); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 82d313e7e706..062410abb689 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -33,6 +33,7 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.splitscreen.SplitScreen; +import com.android.wm.shell.transition.Transitions; import java.util.Optional; @@ -84,6 +85,9 @@ public interface SysUIComponent { @BindsInstance Builder setShellCommandHandler(Optional<ShellCommandHandler> shellDump); + @BindsInstance + Builder setTransitions(Transitions t); + SysUIComponent build(); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java index 7ca8e63bfae1..239a77eb2f45 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java @@ -20,6 +20,7 @@ import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME; import android.content.Context; +import android.hardware.SensorPrivacyManager; import android.os.Handler; import android.os.PowerManager; @@ -62,6 +63,10 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerImpl; +import com.android.systemui.statusbar.policy.SensorPrivacyController; +import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl; import javax.inject.Named; @@ -116,6 +121,25 @@ public abstract class SystemUIDefaultModule { return bC; } + @Provides + @SysUISingleton + static SensorPrivacyController provideSensorPrivacyController( + SensorPrivacyManager sensorPrivacyManager) { + SensorPrivacyController spC = new SensorPrivacyControllerImpl(sensorPrivacyManager); + spC.init(); + return spC; + } + + @Provides + @SysUISingleton + static IndividualSensorPrivacyController provideIndividualSensorPrivacyController( + SensorPrivacyManager sensorPrivacyManager) { + IndividualSensorPrivacyController spC = new IndividualSensorPrivacyControllerImpl( + sensorPrivacyManager); + spC.init(); + return spC; + } + @Binds @SysUISingleton public abstract QSFactory bindQSFactory(QSFactoryImpl qsFactoryImpl); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index ced606c6915e..60b665f0a51a 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -27,6 +27,7 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.splitscreen.SplitScreen; +import com.android.wm.shell.transition.Transitions; import java.util.Optional; @@ -87,4 +88,8 @@ public interface WMComponent { @WMSingleton Optional<TaskViewFactory> getTaskViewFactory(); + + /** Gets transitions */ + @WMSingleton + Transitions getTransitions(); } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index c18a6a45e286..743ac86cbdcb 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -29,6 +29,7 @@ import android.graphics.drawable.Icon; import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.PlaybackState; +import android.os.SystemProperties; import android.util.Log; import android.view.View; import android.view.ViewOutlineProvider; @@ -64,6 +65,11 @@ public class MediaControlPanel { private static final String TAG = "MediaControlPanel"; private static final float DISABLED_ALPHA = 0.38f; + private final boolean mShowAppName = SystemProperties.getBoolean( + "persist.sysui.qs_media_show_app_name", false); + private final boolean mShowDeviceName = SystemProperties.getBoolean( + "persist.sysui.qs_media_show_device_name", false); + private static final Intent SETTINGS_INTENT = new Intent(ACTION_MEDIA_CONTROLS_SETTINGS); // Button IDs for QS controls @@ -265,6 +271,9 @@ public class MediaControlPanel { // App title TextView appName = mViewHolder.getAppName(); appName.setText(data.getApp()); + appName.setVisibility(mShowAppName ? View.VISIBLE : View.GONE); + setVisibleAndAlpha(collapsedSet, R.id.app_name, mShowAppName); + setVisibleAndAlpha(expandedSet, R.id.app_name, mShowAppName); // Artist name TextView artistText = mViewHolder.getArtistText(); @@ -277,6 +286,10 @@ public class MediaControlPanel { mViewHolder.getSeamless().setOnClickListener(v -> { mMediaOutputDialogFactory.create(data.getPackageName(), true); }); + TextView mDeviceName = mViewHolder.getSeamlessText(); + mDeviceName.setVisibility(mShowDeviceName ? View.VISIBLE : View.GONE); + setVisibleAndAlpha(collapsedSet, R.id.media_seamless_text, mShowDeviceName); + setVisibleAndAlpha(expandedSet, R.id.media_seamless_text, mShowDeviceName); ImageView iconView = mViewHolder.getSeamlessIcon(); TextView deviceName = mViewHolder.getSeamlessText(); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index a23b07c5d685..34d1f6e1789c 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -28,6 +28,7 @@ import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.containsType; import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS; +import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; @@ -37,6 +38,7 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BA import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; @@ -91,6 +93,7 @@ import android.view.Surface; import android.view.View; import android.view.ViewTreeObserver; import android.view.WindowInsetsController.Appearance; +import android.view.WindowInsetsController.Behavior; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -159,6 +162,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private static final String EXTRA_DISABLE_STATE = "disabled_state"; private static final String EXTRA_DISABLE2_STATE = "disabled2_state"; private static final String EXTRA_APPEARANCE = "appearance"; + private static final String EXTRA_BEHAVIOR = "behavior"; private static final String EXTRA_TRANSIENT_STATE = "transient_state"; /** Allow some time inbetween the long press for back and recents. */ @@ -209,9 +213,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private boolean mForceNavBarHandleOpaque; private boolean mIsCurrentUserSetup; - /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int) */ + /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */ private @Appearance int mAppearance; + /** @see android.view.WindowInsetsController#setSystemBarsBehavior(int) */ + private @Behavior int mBehavior; + private boolean mTransientShown; private int mNavBarMode = NAV_BAR_MODE_3BUTTON; private LightBarController mLightBarController; @@ -489,6 +496,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mDisabledFlags1 = savedState.getInt(EXTRA_DISABLE_STATE, 0); mDisabledFlags2 = savedState.getInt(EXTRA_DISABLE2_STATE, 0); mAppearance = savedState.getInt(EXTRA_APPEARANCE, 0); + mBehavior = savedState.getInt(EXTRA_BEHAVIOR, 0); mTransientShown = savedState.getBoolean(EXTRA_TRANSIENT_STATE, false); } mSavedState = savedState; @@ -629,6 +637,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1); outState.putInt(EXTRA_DISABLE2_STATE, mDisabledFlags2); outState.putInt(EXTRA_APPEARANCE, mAppearance); + outState.putInt(EXTRA_BEHAVIOR, mBehavior); outState.putBoolean(EXTRA_TRANSIENT_STATE, mTransientShown); if (mNavigationBarView != null) { mNavigationBarView.getLightTransitionsController().saveState(outState); @@ -889,8 +898,9 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } @Override - public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance, - AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { + public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + @Behavior int behavior, boolean isFullscreen) { if (displayId != mDisplayId) { return; } @@ -906,6 +916,10 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mLightBarController.onNavigationBarAppearanceChanged(appearance, nbModeChanged, mNavigationBarMode, navbarColorManagedByIme); } + if (mBehavior != behavior) { + mBehavior = behavior; + updateSystemUiStateFlags(-1); + } } @Override @@ -1319,6 +1333,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, .setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isNavBarWindowVisible()) .setFlag(SYSUI_STATE_IME_SHOWING, (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0) + .setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY, + allowSystemGestureIgnoringBarVisibility()) .commitUpdate(mDisplayId); registerAction(clickable, SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON); registerAction(longClickable, SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER); @@ -1421,6 +1437,10 @@ public class NavigationBar implements View.OnAttachStateChangeListener, return mNavigationBarWindowState == WINDOW_STATE_SHOWING; } + private boolean allowSystemGestureIgnoringBarVisibility() { + return mBehavior != BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; + } + /** * Checks current navigation bar mode and make transitions. */ diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 5eba147ab279..9e7ed0f6e365 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -686,11 +686,12 @@ public class QSPanel extends LinearLayout implements Tunable { */ protected void updateMediaHostContentMargins(ViewGroup mediaHostView) { if (mUsingMediaPlayer) { - int marginStart = mContentMarginStart; + int marginStart = 0; + int marginEnd = 0; if (mUsingHorizontalLayout) { - marginStart = 0; + marginEnd = mContentMarginEnd; } - updateMargins(mediaHostView, marginStart, mContentMarginEnd); + updateMargins(mediaHostView, marginStart, marginEnd); } } 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 ba71fa6a8fb3..9b3775e72f9a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -30,6 +30,7 @@ import com.android.systemui.qs.external.CustomTile; import com.android.systemui.qs.tiles.AirplaneModeTile; import com.android.systemui.qs.tiles.BatterySaverTile; import com.android.systemui.qs.tiles.BluetoothTile; +import com.android.systemui.qs.tiles.CameraToggleTile; import com.android.systemui.qs.tiles.CastTile; import com.android.systemui.qs.tiles.CellularTile; import com.android.systemui.qs.tiles.ColorInversionTile; @@ -39,6 +40,7 @@ import com.android.systemui.qs.tiles.FlashlightTile; import com.android.systemui.qs.tiles.HotspotTile; import com.android.systemui.qs.tiles.InternetTile; import com.android.systemui.qs.tiles.LocationTile; +import com.android.systemui.qs.tiles.MicrophoneToggleTile; import com.android.systemui.qs.tiles.NfcTile; import com.android.systemui.qs.tiles.NightDisplayTile; import com.android.systemui.qs.tiles.ReduceBrightColorsTile; @@ -83,6 +85,8 @@ public class QSFactoryImpl implements QSFactory { private final Provider<UiModeNightTile> mUiModeNightTileProvider; private final Provider<ScreenRecordTile> mScreenRecordTileProvider; private final Provider<ReduceBrightColorsTile> mReduceBrightColorsTileProvider; + private final Provider<CameraToggleTile> mCameraToggleTileProvider; + private final Provider<MicrophoneToggleTile> mMicrophoneToggleTileProvider; private final Lazy<QSHost> mQsHostLazy; private final Provider<CustomTile.Builder> mCustomTileBuilderProvider; @@ -115,7 +119,9 @@ public class QSFactoryImpl implements QSFactory { Provider<GarbageMonitor.MemoryTile> memoryTileProvider, Provider<UiModeNightTile> uiModeNightTileProvider, Provider<ScreenRecordTile> screenRecordTileProvider, - Provider<ReduceBrightColorsTile> reduceBrightColorsTileProvider) { + Provider<ReduceBrightColorsTile> reduceBrightColorsTileProvider, + Provider<CameraToggleTile> cameraToggleTileProvider, + Provider<MicrophoneToggleTile> microphoneToggleTileProvider) { mQsHostLazy = qsHostLazy; mCustomTileBuilderProvider = customTileBuilderProvider; @@ -143,6 +149,8 @@ public class QSFactoryImpl implements QSFactory { mUiModeNightTileProvider = uiModeNightTileProvider; mScreenRecordTileProvider = screenRecordTileProvider; mReduceBrightColorsTileProvider = reduceBrightColorsTileProvider; + mCameraToggleTileProvider = cameraToggleTileProvider; + mMicrophoneToggleTileProvider = microphoneToggleTileProvider; } public QSTile createTile(String tileSpec) { @@ -198,6 +206,10 @@ public class QSFactoryImpl implements QSFactory { return mScreenRecordTileProvider.get(); case "reduce_brightness": return mReduceBrightColorsTileProvider.get(); + case "cameratoggle": + return mCameraToggleTileProvider.get(); + case "mictoggle": + return mMicrophoneToggleTileProvider.get(); } // Custom tiles diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java new file mode 100644 index 000000000000..98740a20a7c0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA; + +import static com.android.systemui.DejankUtils.whitelistIpcs; + +import android.annotation.StringRes; +import android.os.Handler; +import android.os.Looper; +import android.provider.DeviceConfig; + +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; + +import com.android.internal.logging.MetricsLogger; +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; + +import javax.inject.Inject; + +public class CameraToggleTile extends SensorPrivacyToggleTile { + + @Inject + protected CameraToggleTile(QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger, + IndividualSensorPrivacyController sensorPrivacyController) { + super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController, + activityStarter, qsLogger, sensorPrivacyController); + } + + @Override + public boolean isAvailable() { + return /*getHost().getContext().getPackageManager().hasSystemFeature(FEATURE_CAMERA_TOGGLE) + && */whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + "camera_toggle_enabled", + false)); + } + + @Override + public @DrawableRes int getIconRes() { + return R.drawable.ic_camera_blocked; + } + + @Override + public @NonNull CharSequence getTileLabel() { + return mContext.getString(R.string.quick_settings_camera_label); + } + + @Override + public int getSensorId() { + return CAMERA; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java new file mode 100644 index 000000000000..8cc0d7b4e8b8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE; + +import static com.android.systemui.DejankUtils.whitelistIpcs; + +import android.os.Handler; +import android.os.Looper; +import android.provider.DeviceConfig; + +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; + +import com.android.internal.logging.MetricsLogger; +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; + +import javax.inject.Inject; + +public class MicrophoneToggleTile extends SensorPrivacyToggleTile { + + @Inject + protected MicrophoneToggleTile(QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger, + IndividualSensorPrivacyController sensorPrivacyController) { + super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController, + activityStarter, qsLogger, sensorPrivacyController); + } + + @Override + public boolean isAvailable() { + return whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + "mic_toggle_enabled", + false)); + } + + @Override + public @DrawableRes int getIconRes() { + return R.drawable.ic_mic_blocked; + } + + @Override + public @NonNull CharSequence getTileLabel() { + return mContext.getString(R.string.quick_settings_mic_label); + } + + @Override + public int getSensorId() { + return MICROPHONE; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java new file mode 100644 index 000000000000..12205d6483a8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.content.Intent; +import android.hardware.SensorPrivacyManager.IndividualSensor; +import android.os.Handler; +import android.os.Looper; +import android.service.quicksettings.Tile; +import android.widget.Switch; + +import androidx.annotation.DrawableRes; + +import com.android.internal.logging.MetricsLogger; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.qs.QSTile; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; + +/** + * Superclass to toggle individual sensor privacy via quick settings tiles + */ +public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanState> implements + IndividualSensorPrivacyController.Callback { + + private IndividualSensorPrivacyController mSensorPrivacyController; + + /** + * @return Id of the sensor that will be toggled + */ + public abstract @IndividualSensor int getSensorId(); + + /** + * @return icon for the QS tile + */ + public abstract @DrawableRes int getIconRes(); + + protected SensorPrivacyToggleTile(QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger, + IndividualSensorPrivacyController sensorPrivacyController) { + super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController, + activityStarter, qsLogger); + mSensorPrivacyController = sensorPrivacyController; + mSensorPrivacyController.observe(getLifecycle(), this); + } + + @Override + public BooleanState newTileState() { + return new BooleanState(); + } + + @Override + protected void handleClick() { + mSensorPrivacyController.setSensorBlocked(getSensorId(), + !mSensorPrivacyController.isSensorBlocked(getSensorId())); + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + boolean isBlocked = arg == null ? mSensorPrivacyController.isSensorBlocked(getSensorId()) + : (boolean) arg; + + state.icon = ResourceIcon.get(getIconRes()); + state.state = isBlocked ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; + state.value = isBlocked; + state.label = getTileLabel(); + state.handlesLongClick = false; + state.contentDescription = state.label; + state.expandedAccessibilityClassName = Switch.class.getName(); + } + + @Override + public int getMetricsCategory() { + return 0; + } + + @Override + public Intent getLongClickIntent() { + return null; + } + + @Override + public void onSensorBlockedChanged(int sensor, boolean blocked) { + if (sensor == getSensorId()) { + refreshState(blocked); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 54e30af675ab..760ebadf1b99 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -56,12 +56,14 @@ import android.os.Looper; import android.os.PatternMatcher; import android.os.RemoteException; import android.os.UserHandle; +import android.util.ArraySet; import android.util.Log; import android.view.InputMonitor; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; import android.view.accessibility.AccessibilityManager; +import android.window.IRemoteTransition; import androidx.annotation.NonNull; @@ -86,6 +88,7 @@ import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputMonitorCompat; import com.android.systemui.shared.system.QuickStepContract; +import com.android.systemui.shared.system.RemoteTransitionCompat; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; @@ -93,9 +96,9 @@ import com.android.systemui.statusbar.phone.StatusBarWindowCallback; import com.android.systemui.statusbar.policy.CallbackController; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; -import com.android.wm.shell.onehanded.OneHandedEvents; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; +import com.android.wm.shell.transition.Transitions; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -142,6 +145,7 @@ public class OverviewProxyService extends CurrentUserTracker implements private final ScreenshotHelper mScreenshotHelper; private final Optional<OneHanded> mOneHandedOptional; private final CommandQueue mCommandQueue; + private final Transitions mShellTransitions; private Region mActiveNavBarRegion; @@ -158,6 +162,7 @@ public class OverviewProxyService extends CurrentUserTracker implements private float mWindowCornerRadius; private boolean mSupportsRoundedCornersOnWindows; private int mNavBarMode = NAV_BAR_MODE_3BUTTON; + private final ArraySet<IRemoteTransition> mRemoteTransitions = new ArraySet<>(); @VisibleForTesting public ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() { @@ -463,8 +468,7 @@ public class OverviewProxyService extends CurrentUserTracker implements } final long token = Binder.clearCallingIdentity(); try { - mOneHandedOptional.ifPresent(oneHanded -> oneHanded.stopOneHanded( - OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT)); + mOneHandedOptional.ifPresent(oneHanded -> oneHanded.stopOneHanded()); } finally { Binder.restoreCallingIdentity(token); } @@ -530,6 +534,31 @@ public class OverviewProxyService extends CurrentUserTracker implements } } + @Override + public void registerRemoteTransition(RemoteTransitionCompat remoteTransition) { + if (!verifyCaller("registerRemoteTransition")) return; + final long binderToken = Binder.clearCallingIdentity(); + try { + mRemoteTransitions.add(remoteTransition.getTransition()); + mShellTransitions.registerRemote( + remoteTransition.getFilter(), remoteTransition.getTransition()); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + + @Override + public void unregisterRemoteTransition(RemoteTransitionCompat remoteTransition) { + if (!verifyCaller("registerRemoteTransition")) return; + final long binderToken = Binder.clearCallingIdentity(); + try { + mRemoteTransitions.remove(remoteTransition.getTransition()); + mShellTransitions.unregisterRemote(remoteTransition.getTransition()); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + private boolean verifyCaller(String reason) { final int callerId = Binder.getCallingUserHandle().getIdentifier(); if (callerId != mCurrentBoundedUserId) { @@ -639,7 +668,8 @@ public class OverviewProxyService extends CurrentUserTracker implements Optional<LegacySplitScreen> splitScreenOptional, Optional<Lazy<StatusBar>> statusBarOptionalLazy, Optional<OneHanded> oneHandedOptional, - BroadcastDispatcher broadcastDispatcher) { + BroadcastDispatcher broadcastDispatcher, + Transitions shellTransitions) { super(broadcastDispatcher); mContext = context; mPipOptional = pipOptional; @@ -658,6 +688,7 @@ public class OverviewProxyService extends CurrentUserTracker implements mSysUiState = sysUiState; mSysUiState.addCallback(this::notifySystemUiStateFlags); mOneHandedOptional = oneHandedOptional; + mShellTransitions = shellTransitions; // Assumes device always starts with back button until launcher tells it that it does not mNavBarButtonAlpha = 1.0f; @@ -806,6 +837,12 @@ public class OverviewProxyService extends CurrentUserTracker implements // Clean up the minimized state if launcher dies mSplitScreenOptional.ifPresent( splitScreen -> splitScreen.setMinimized(false)); + + // Clean up any registered remote transitions + for (int i = mRemoteTransitions.size() - 1; i >= 0; --i) { + mShellTransitions.unregisterRemote(mRemoteTransitions.valueAt(i)); + } + mRemoteTransitions.clear(); } public void startConnectionToCurrentUser() { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java index db2750b8842f..9dce19192dbe 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java @@ -47,7 +47,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.systemui.R; import com.android.systemui.SystemUIFactory; -import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ShareTransition; +import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -79,13 +79,13 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { private final String mScreenshotId; private final boolean mSmartActionsEnabled; private final Random mRandom = new Random(); - private final Supplier<ShareTransition> mSharedElementTransition; + private final Supplier<ActionTransition> mSharedElementTransition; private final ImageExporter mImageExporter; SaveImageInBackgroundTask(Context context, ImageExporter exporter, ScreenshotSmartActions screenshotSmartActions, ScreenshotController.SaveImageInBackgroundData data, - Supplier<ShareTransition> sharedElementTransition) { + Supplier<ActionTransition> sharedElementTransition) { mContext = context; mScreenshotSmartActions = screenshotSmartActions; mImageData = new ScreenshotController.SavedImageData(); @@ -150,7 +150,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { mImageData.uri = uri; mImageData.smartActions = smartActions; mImageData.shareTransition = createShareAction(mContext, mContext.getResources(), uri); - mImageData.editAction = createEditAction(mContext, mContext.getResources(), uri); + mImageData.editTransition = createEditAction(mContext, mContext.getResources(), uri); mImageData.deleteAction = createDeleteAction(mContext, mContext.getResources(), uri); mParams.mActionsReadyListener.onActionsReady(mImageData); @@ -204,9 +204,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { * Assumes that the action intent is sent immediately after being supplied. */ @VisibleForTesting - Supplier<ShareTransition> createShareAction(Context context, Resources r, Uri uri) { + Supplier<ActionTransition> createShareAction(Context context, Resources r, Uri uri) { return () -> { - ShareTransition transition = mSharedElementTransition.get(); + ActionTransition transition = mSharedElementTransition.get(); // Note: Both the share and edit actions are proxied through ActionProxyReceiver in // order to do some common work like dismissing the keyguard and sending @@ -259,52 +259,57 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { Icon.createWithResource(r, R.drawable.ic_screenshot_share), r.getString(com.android.internal.R.string.share), shareAction); - transition.shareAction = shareActionBuilder.build(); + transition.action = shareActionBuilder.build(); return transition; }; } @VisibleForTesting - Notification.Action createEditAction(Context context, Resources r, Uri uri) { - // Note: Both the share and edit actions are proxied through ActionProxyReceiver in - // order to do some common work like dismissing the keyguard and sending - // closeSystemWindows - - // Create an edit intent, if a specific package is provided as the editor, then - // launch that directly - String editorPackage = context.getString(R.string.config_screenshotEditor); - Intent editIntent = new Intent(Intent.ACTION_EDIT); - if (!TextUtils.isEmpty(editorPackage)) { - editIntent.setComponent(ComponentName.unflattenFromString(editorPackage)); - } - editIntent.setDataAndType(uri, "image/png"); - editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + Supplier<ActionTransition> createEditAction(Context context, Resources r, Uri uri) { + return () -> { + ActionTransition transition = mSharedElementTransition.get(); + // Note: Both the share and edit actions are proxied through ActionProxyReceiver in + // order to do some common work like dismissing the keyguard and sending + // closeSystemWindows - PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, 0, - editIntent, PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT); + // Create an edit intent, if a specific package is provided as the editor, then + // launch that directly + String editorPackage = context.getString(R.string.config_screenshotEditor); + Intent editIntent = new Intent(Intent.ACTION_EDIT); + if (!TextUtils.isEmpty(editorPackage)) { + editIntent.setComponent(ComponentName.unflattenFromString(editorPackage)); + } + editIntent.setDataAndType(uri, "image/png"); + editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - // Make sure pending intents for the system user are still unique across users - // by setting the (otherwise unused) request code to the current user id. - int requestCode = mContext.getUserId(); + PendingIntent pendingIntent = PendingIntent.getActivityAsUser( + context, 0, editIntent, PendingIntent.FLAG_IMMUTABLE, + transition.bundle, UserHandle.CURRENT); - // Create a edit action - PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode, - new Intent(context, ActionProxyReceiver.class) - .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, pendingIntent) - .putExtra(ScreenshotController.EXTRA_ID, mScreenshotId) - .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED, - mSmartActionsEnabled) - .setAction(Intent.ACTION_EDIT) - .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), - PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, - UserHandle.SYSTEM); - Notification.Action.Builder editActionBuilder = new Notification.Action.Builder( - Icon.createWithResource(r, R.drawable.ic_screenshot_edit), - r.getString(com.android.internal.R.string.screenshot_edit), editAction); + // Make sure pending intents for the system user are still unique across users + // by setting the (otherwise unused) request code to the current user id. + int requestCode = mContext.getUserId(); + + // Create a edit action + PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode, + new Intent(context, ActionProxyReceiver.class) + .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, pendingIntent) + .putExtra(ScreenshotController.EXTRA_ID, mScreenshotId) + .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED, + mSmartActionsEnabled) + .setAction(Intent.ACTION_EDIT) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, + UserHandle.SYSTEM); + Notification.Action.Builder editActionBuilder = new Notification.Action.Builder( + Icon.createWithResource(r, R.drawable.ic_screenshot_edit), + r.getString(com.android.internal.R.string.screenshot_edit), editAction); - return editActionBuilder.build(); + transition.action = editActionBuilder.build(); + return transition; + }; } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 7d5779949074..d77d1ea75f30 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -81,7 +81,7 @@ import com.android.settingslib.applications.InterestingConfigChanges; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ShareTransition; +import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; import com.android.systemui.util.DeviceConfigProxy; import java.util.List; @@ -115,17 +115,17 @@ public class ScreenshotController { */ static class SavedImageData { public Uri uri; - public Supplier<ShareTransition> shareTransition; - public Notification.Action editAction; + public Supplier<ActionTransition> shareTransition; + public Supplier<ActionTransition> editTransition; public Notification.Action deleteAction; public List<Notification.Action> smartActions; /** - * POD for shared element transition to share sheet. + * POD for shared element transition. */ - static class ShareTransition { + static class ActionTransition { public Bundle bundle; - public Notification.Action shareAction; + public Notification.Action action; public Runnable onCancelRunnable; } @@ -135,7 +135,7 @@ public class ScreenshotController { public void reset() { uri = null; shareTransition = null; - editAction = null; + editTransition = null; deleteAction = null; smartActions = null; } @@ -352,6 +352,10 @@ public class ScreenshotController { } } + boolean isPendingSharedTransition() { + return mScreenshotView.isPendingSharedTransition(); + } + /** * Release the constructed window context. */ @@ -626,7 +630,7 @@ public class ScreenshotController { } mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mImageExporter, - mScreenshotSmartActions, data, getShareTransitionSupplier()); + mScreenshotSmartActions, data, getActionTransitionSupplier()); mSaveInBgTask.execute(); } @@ -680,7 +684,7 @@ public class ScreenshotController { * Supplies the necessary bits for the shared element transition to share sheet. * Note that once supplied, the action intent to share must be sent immediately after. */ - private Supplier<ShareTransition> getShareTransitionSupplier() { + private Supplier<ActionTransition> getActionTransitionSupplier() { return () -> { ExitTransitionCallbacks cb = new ExitTransitionCallbacks() { @Override @@ -689,7 +693,13 @@ public class ScreenshotController { } @Override - public void onFinish() { } + public void hideSharedElements() { + resetScreenshotView(); + } + + @Override + public void onFinish() { + } }; Pair<ActivityOptions, ExitTransitionCoordinator> transition = @@ -698,7 +708,7 @@ public class ScreenshotController { ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME)); transition.second.startExit(); - ShareTransition supply = new ShareTransition(); + ActionTransition supply = new ActionTransition(); supply.bundle = transition.first.toBundle(); supply.onCancelRunnable = () -> ActivityOptions.stopSharedElementAnimation(mWindow); return supply; diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index 41c2098d2505..211f5072bd1a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -74,7 +74,7 @@ import android.widget.LinearLayout; import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; -import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ShareTransition; +import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; import com.android.systemui.shared.system.QuickStepContract; import java.util.ArrayList; @@ -106,7 +106,6 @@ public class ScreenshotView extends FrameLayout implements private static final long SCREENSHOT_DISMISS_Y_DURATION_MS = 350; private static final long SCREENSHOT_DISMISS_ALPHA_DURATION_MS = 183; private static final long SCREENSHOT_DISMISS_ALPHA_OFFSET_MS = 50; // delay before starting fade - private static final long SCREENSHOT_DISMISS_SHARE_OFFSET_MS = 300; // delay after share clicked private static final float SCREENSHOT_ACTIONS_START_SCALE_X = .7f; private static final float ROUNDED_CORNER_RADIUS = .05f; private static final int SWIPE_PADDING_DP = 12; // extra padding around views to allow swipe @@ -142,7 +141,7 @@ public class ScreenshotView extends FrameLayout implements private UiEventLogger mUiEventLogger; private ScreenshotViewCallback mCallbacks; private Animator mDismissAnimation; - private boolean mIgnoreDismiss; + private boolean mPendingSharedTransition; private final ArrayList<ScreenshotActionChip> mSmartChips = new ArrayList<>(); private PendingInteraction mPendingInteraction; @@ -296,6 +295,10 @@ public class ScreenshotView extends FrameLayout implements requestFocus(); } + View getScreenshotPreview() { + return mScreenshotPreview; + } + /** * Set up the logger and callback on dismissal. * @@ -535,44 +538,22 @@ public class ScreenshotView extends FrameLayout implements }); return animator; } - protected View getScreenshotPreview() { - return mScreenshotPreview; - } void setChipIntents(ScreenshotController.SavedImageData imageData) { mShareChip.setOnClickListener(v -> { - ShareTransition transition = imageData.shareTransition.get(); - try { - mIgnoreDismiss = true; - transition.shareAction.actionIntent.send(); - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED); - - // Ensures that we delay dismissing until transition has started. - postDelayed(() -> { - mIgnoreDismiss = false; - animateDismissal(); - }, SCREENSHOT_DISMISS_SHARE_OFFSET_MS); - } catch (PendingIntent.CanceledException e) { - mIgnoreDismiss = false; - if (transition.onCancelRunnable != null) { - transition.onCancelRunnable.run(); - } - Log.e(TAG, "Share intent cancelled", e); - } + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED); + startSharedTransition( + imageData.shareTransition.get()); + }); + mEditChip.setOnClickListener(v -> { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED); + startSharedTransition( + imageData.editTransition.get()); }); - mEditChip.setPendingIntent(imageData.editAction.actionIntent, - () -> { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED); - animateDismissal(); - }); mScreenshotPreview.setOnClickListener(v -> { - try { - imageData.editAction.actionIntent.send(); - } catch (PendingIntent.CanceledException e) { - Log.e(TAG, "PendingIntent was cancelled", e); - } mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED); - animateDismissal(); + startSharedTransition( + imageData.editTransition.get()); }); if (mPendingInteraction != null) { @@ -611,14 +592,15 @@ public class ScreenshotView extends FrameLayout implements return (mDismissAnimation != null && mDismissAnimation.isRunning()); } + boolean isPendingSharedTransition() { + return mPendingSharedTransition; + } + void animateDismissal() { - animateDismissal(createScreenshotDismissAnimation()); + animateDismissal(createScreenshotTranslateDismissAnimation()); } private void animateDismissal(Animator dismissAnimation) { - if (mIgnoreDismiss) { - return; - } if (DEBUG_WINDOW) { Log.d(TAG, "removing OnComputeInternalInsetsListener"); } @@ -671,6 +653,7 @@ public class ScreenshotView extends FrameLayout implements getViewTreeObserver().removeOnComputeInternalInsetsListener(this); // Clear any references to the bitmap mScreenshotPreview.setImageDrawable(null); + mPendingSharedTransition = false; mActionsContainerBackground.setVisibility(View.GONE); mActionsContainer.setVisibility(View.GONE); mBackgroundProtection.setAlpha(0f); @@ -698,7 +681,23 @@ public class ScreenshotView extends FrameLayout implements mScreenshotSelectorView.stop(); } - private AnimatorSet createScreenshotDismissAnimation() { + private void startSharedTransition(ActionTransition transition) { + try { + mPendingSharedTransition = true; + transition.action.actionIntent.send(); + + // fade out non-preview UI + createScreenshotFadeDismissAnimation().start(); + } catch (PendingIntent.CanceledException e) { + mPendingSharedTransition = false; + if (transition.onCancelRunnable != null) { + transition.onCancelRunnable.run(); + } + Log.e(TAG, "Intent cancelled", e); + } + } + + private AnimatorSet createScreenshotTranslateDismissAnimation() { ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1); alphaAnim.setStartDelay(SCREENSHOT_DISMISS_ALPHA_OFFSET_MS); alphaAnim.setDuration(SCREENSHOT_DISMISS_ALPHA_DURATION_MS); @@ -725,6 +724,19 @@ public class ScreenshotView extends FrameLayout implements return animSet; } + private ValueAnimator createScreenshotFadeDismissAnimation() { + ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1); + alphaAnim.addUpdateListener(animation -> { + float alpha = 1 - animation.getAnimatedFraction(); + mDismissButton.setAlpha(alpha); + mActionsContainerBackground.setAlpha(alpha); + mActionsContainer.setAlpha(alpha); + mBackgroundProtection.setAlpha(alpha); + }); + alphaAnim.setDuration(600); + return alphaAnim; + } + /** * Create a drawable using the size of the bitmap and insets as the fractional inset parameters. */ diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java index c33bbc51ed5b..144ad39a32aa 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -72,7 +72,9 @@ public class TakeScreenshotService extends Service { if (DEBUG_DISMISS) { Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS"); } - mScreenshot.dismissScreenshot(false); + if (!mScreenshot.isPendingSharedTransition()) { + mScreenshot.dismissScreenshot(false); + } } } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index fbb80428d68c..c4fa6df56775 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -34,7 +34,6 @@ import android.app.StatusBarManager.WindowType; import android.app.StatusBarManager.WindowVisibleState; import android.content.ComponentName; import android.content.Context; -import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.display.DisplayManager; @@ -50,6 +49,7 @@ import android.util.Pair; import android.util.SparseArray; import android.view.InsetsState.InternalInsetsType; import android.view.WindowInsetsController.Appearance; +import android.view.WindowInsetsController.Behavior; import androidx.annotation.NonNull; @@ -90,7 +90,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< private static final int MSG_EXPAND_NOTIFICATIONS = 3 << MSG_SHIFT; private static final int MSG_COLLAPSE_PANELS = 4 << MSG_SHIFT; private static final int MSG_EXPAND_SETTINGS = 5 << MSG_SHIFT; - private static final int MSG_SYSTEM_BAR_APPEARANCE_CHANGED = 6 << MSG_SHIFT; + private static final int MSG_SYSTEM_BAR_CHANGED = 6 << MSG_SHIFT; private static final int MSG_DISPLAY_READY = 7 << MSG_SHIFT; private static final int MSG_SHOW_IME_BUTTON = 8 << MSG_SHIFT; private static final int MSG_TOGGLE_RECENT_APPS = 9 << MSG_SHIFT; @@ -131,17 +131,16 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< private static final int MSG_RECENTS_ANIMATION_STATE_CHANGED = 47 << MSG_SHIFT; private static final int MSG_SHOW_TRANSIENT = 48 << MSG_SHIFT; private static final int MSG_ABORT_TRANSIENT = 49 << MSG_SHIFT; - private static final int MSG_TOP_APP_WINDOW_CHANGED = 50 << MSG_SHIFT; - private static final int MSG_SHOW_INATTENTIVE_SLEEP_WARNING = 51 << MSG_SHIFT; - private static final int MSG_DISMISS_INATTENTIVE_SLEEP_WARNING = 52 << MSG_SHIFT; - private static final int MSG_SHOW_TOAST = 53 << MSG_SHIFT; - private static final int MSG_HIDE_TOAST = 54 << MSG_SHIFT; - private static final int MSG_TRACING_STATE_CHANGED = 55 << MSG_SHIFT; - private static final int MSG_SUPPRESS_AMBIENT_DISPLAY = 56 << MSG_SHIFT; - private static final int MSG_REQUEST_WINDOW_MAGNIFICATION_CONNECTION = 57 << MSG_SHIFT; - private static final int MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND = 58 << MSG_SHIFT; + private static final int MSG_SHOW_INATTENTIVE_SLEEP_WARNING = 50 << MSG_SHIFT; + private static final int MSG_DISMISS_INATTENTIVE_SLEEP_WARNING = 51 << MSG_SHIFT; + private static final int MSG_SHOW_TOAST = 52 << MSG_SHIFT; + private static final int MSG_HIDE_TOAST = 53 << MSG_SHIFT; + private static final int MSG_TRACING_STATE_CHANGED = 54 << MSG_SHIFT; + private static final int MSG_SUPPRESS_AMBIENT_DISPLAY = 55 << MSG_SHIFT; + private static final int MSG_REQUEST_WINDOW_MAGNIFICATION_CONNECTION = 56 << MSG_SHIFT; + private static final int MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND = 57 << MSG_SHIFT; //TODO(b/169175022) Update name and when feature name is locked. - private static final int MSG_EMERGENCY_ACTION_LAUNCH_GESTURE = 59 << MSG_SHIFT; + private static final int MSG_EMERGENCY_ACTION_LAUNCH_GESTURE = 58 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -308,10 +307,11 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< default void onRecentsAnimationStateChanged(boolean running) { } /** - * @see IStatusBar#onSystemBarAppearanceChanged(int, int, AppearanceRegion[], boolean). + * @see IStatusBar#onSystemBarAttributesChanged. */ - default void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance, - AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { } + default void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + @Behavior int behavior, boolean isFullscreen) { } /** * @see IStatusBar#showTransient(int, int[]). @@ -324,12 +324,6 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< default void abortTransient(int displayId, @InternalInsetsType int[] types) { } /** - * @see IStatusBar#topAppWindowChanged(int, boolean, boolean). - */ - default void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) { - } - - /** * Called to notify System UI that a warning about the device going to sleep * due to prolonged user inactivity should be shown. */ @@ -547,18 +541,6 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } @Override - public void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) { - synchronized (mLock) { - SomeArgs args = SomeArgs.obtain(); - args.argi1 = displayId; - args.argi2 = isFullscreen ? 1 : 0; - args.argi3 = isImmersive ? 1 : 0; - mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, args).sendToTarget(); - } - - } - - @Override public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher, boolean isMultiClientImeEnabled) { synchronized (mLock) { @@ -969,15 +951,18 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } @Override - public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance, - AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { + public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + @Behavior int behavior, boolean isFullscreen) { synchronized (mLock) { SomeArgs args = SomeArgs.obtain(); args.argi1 = displayId; args.argi2 = appearance; args.argi3 = navbarColorManagedByIme ? 1 : 0; args.arg1 = appearanceRegions; - mHandler.obtainMessage(MSG_SYSTEM_BAR_APPEARANCE_CHANGED, args).sendToTarget(); + args.argi4 = behavior; + args.argi5 = isFullscreen ? 1 : 0; + mHandler.obtainMessage(MSG_SYSTEM_BAR_CHANGED, args).sendToTarget(); } } @@ -1328,11 +1313,12 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< mCallbacks.get(i).onRecentsAnimationStateChanged(msg.arg1 > 0); } break; - case MSG_SYSTEM_BAR_APPEARANCE_CHANGED: + case MSG_SYSTEM_BAR_CHANGED: args = (SomeArgs) msg.obj; for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).onSystemBarAppearanceChanged(args.argi1, args.argi2, - (AppearanceRegion[]) args.arg1, args.argi3 == 1); + mCallbacks.get(i).onSystemBarAttributesChanged(args.argi1, args.argi2, + (AppearanceRegion[]) args.arg1, args.argi3 == 1, args.argi4, + args.argi5 == 1); } args.recycle(); break; @@ -1352,15 +1338,6 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } break; } - case MSG_TOP_APP_WINDOW_CHANGED: { - args = (SomeArgs) msg.obj; - for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).topAppWindowChanged( - args.argi1, args.argi2 != 0, args.argi3 != 0); - } - args.recycle(); - break; - } case MSG_SHOW_INATTENTIVE_SLEEP_WARNING: for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).showInattentiveSleepWarning(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index e9442499a8ce..6ba52156c374 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -88,11 +88,6 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll private boolean mIsFullscreen = false; /** - * If the navigation bar can stay hidden when the display gets tapped. - */ - private boolean mIsImmersive = false; - - /** * If the device is currently pulsing (AOD2). */ private boolean mPulsing; @@ -360,13 +355,12 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll } @Override - public void setFullscreenState(boolean isFullscreen, boolean isImmersive) { - if (mIsFullscreen != isFullscreen || mIsImmersive != isImmersive) { + public void setFullscreenState(boolean isFullscreen) { + if (mIsFullscreen != isFullscreen) { mIsFullscreen = isFullscreen; - mIsImmersive = isImmersive; synchronized (mListeners) { for (RankedListener rl : new ArrayList<>(mListeners)) { - rl.mListener.onFullscreenStateChanged(isFullscreen, isImmersive); + rl.mListener.onFullscreenStateChanged(isFullscreen, true /* isImmersive */); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java index 9f8fe35dfbc9..a2e07b289e9d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java @@ -121,7 +121,7 @@ public interface SysuiStatusBarStateController extends StatusBarStateController /** * Set the fullscreen state */ - void setFullscreenState(boolean isFullscreen, boolean isImmersive); + void setFullscreenState(boolean isFullscreen); /** * Set pulsing 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 8223d9717208..a03fc136da61 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 @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.row; +import static android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY; import static android.service.notification.NotificationListenerService.REASON_CANCEL; import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; @@ -1785,6 +1786,27 @@ public class ExpandableNotificationRow extends ActivatableNotificationView doLongClickCallback(x, y, menuItem); } + /** + * Perform a smart action which triggers a longpress (expose guts). + * Based on the semanticAction passed, may update the state of the guts view. + * @param semanticAction associated with this smart action click + */ + public void doSmartActionClick(int x, int y, int semanticAction) { + createMenu(); + NotificationMenuRowPlugin provider = getProvider(); + MenuItem menuItem = null; + if (provider != null) { + menuItem = provider.getLongpressMenuItem(mContext); + } + if (SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY == semanticAction + && menuItem.getGutsView() instanceof NotificationConversationInfo) { + NotificationConversationInfo info = + (NotificationConversationInfo) menuItem.getGutsView(); + info.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE); + } + doLongClickCallback(x, y, menuItem); + } + private void doLongClickCallback(int x, int y, MenuItem menuItem) { if (mLongPressListener != null && menuItem != null) { mLongPressListener.onLongPress(this, x, y, menuItem); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index 1a2550b81878..adeba9078c52 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -206,6 +206,7 @@ public class NotificationConversationInfo extends LinearLayout implements } public void bindNotification( + @Action int selectedAction, ShortcutManager shortcutManager, PackageManager pm, INotificationManager iNotificationManager, @@ -224,7 +225,8 @@ public class NotificationConversationInfo extends LinearLayout implements @Background Handler bgHandler, OnConversationSettingsClickListener onConversationSettingsClickListener, Optional<BubblesManager> bubblesManagerOptional) { - mSelectedAction = -1; + mPressedApply = false; + mSelectedAction = selectedAction; mINotificationManager = iNotificationManager; mOnUserInteractionCallback = onUserInteractionCallback; mPackageName = pkg; @@ -297,7 +299,8 @@ public class NotificationConversationInfo extends LinearLayout implements settingsButton.setOnClickListener(getSettingsOnClickListener()); settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE); - updateToggleActions(getSelectedAction(), false); + updateToggleActions(mSelectedAction == -1 ? getPriority() : mSelectedAction, + false); } private void bindHeader() { @@ -406,7 +409,7 @@ public class NotificationConversationInfo extends LinearLayout implements @Override public void onFinishedClosing() { - // TODO: do we need to do anything here? + mSelectedAction = -1; } @Override @@ -487,7 +490,7 @@ public class NotificationConversationInfo extends LinearLayout implements throw new IllegalArgumentException("Unrecognized behavior: " + mSelectedAction); } - boolean isAChange = getSelectedAction() != selectedAction; + boolean isAChange = getPriority() != selectedAction; TextView done = findViewById(R.id.done); done.setText(isAChange ? R.string.inline_ok_button @@ -498,6 +501,10 @@ public class NotificationConversationInfo extends LinearLayout implements } int getSelectedAction() { + return mSelectedAction; + } + + private int getPriority() { if (mNotificationChannel.getImportance() <= IMPORTANCE_LOW && mNotificationChannel.getImportance() > IMPORTANCE_UNSPECIFIED) { return ACTION_MUTE; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 2fd17a587612..6a873b678a93 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -474,6 +474,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx R.dimen.notification_guts_conversation_icon_size)); notificationInfoView.bindNotification( + notificationInfoView.getSelectedAction(), mShortcutManager, pmUser, mNotificationManager, 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 2c501cbe9846..b5963ef443e7 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 @@ -77,6 +77,7 @@ public class AmbientState { private Runnable mOnPulseHeightChangedListener; private ExpandableNotificationRow mTrackedHeadsUpRow; private float mAppearFraction; + private boolean mIsShadeOpening; /** Tracks the state from AlertingNotificationManager#hasNotifications() */ private boolean mHasAlertEntries; @@ -96,6 +97,14 @@ public class AmbientState { mBaseZHeight = getBaseHeight(mZDistanceBetweenElements); } + void setIsShadeOpening(boolean isOpening) { + mIsShadeOpening = isOpening; + } + + public boolean isShadeOpening() { + return mIsShadeOpening; + } + private static int getZDistanceBetweenElements(Context context) { return Math.max(1, context.getResources() .getDimensionPixelSize(R.dimen.z_distance_between_notifications)); 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 9e8385b8a61b..6abbc6bc2c36 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 @@ -550,6 +550,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } + void setIsShadeOpening(boolean isOpening) { + mAmbientState.setIsShadeOpening(isOpening); + } + @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) protected void onFinishInflate() { 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 f4d01f320e46..5126f5557b7a 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 @@ -271,6 +271,10 @@ public class NotificationStackScrollLayoutController { } }; + public void setIsShadeOpening(boolean isOpening) { + mView.setIsShadeOpening(isOpening); + } + private final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() { @Override public void onMenuClicked( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java index d27a3d53c0a2..7d134057ee76 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java @@ -22,7 +22,8 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.Nullable; import android.view.View; -import android.view.WindowInsetsController; +import android.view.WindowInsetsController.Appearance; +import android.view.WindowInsetsController.Behavior; import android.view.WindowManager; import android.view.animation.AccelerateInterpolator; @@ -52,7 +53,7 @@ public class LightsOutNotifController { private final WindowManager mWindowManager; /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int) */ - @VisibleForTesting @WindowInsetsController.Appearance int mAppearance; + @VisibleForTesting @Appearance int mAppearance; private int mDisplayId; private View mLightsOutNotifView; @@ -146,10 +147,9 @@ public class LightsOutNotifController { private final CommandQueue.Callbacks mCallback = new CommandQueue.Callbacks() { @Override - public void onSystemBarAppearanceChanged(int displayId, - @WindowInsetsController.Appearance int appearance, - AppearanceRegion[] appearanceRegions, - boolean navbarColorManagedByIme) { + public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + @Behavior int behavior, boolean isFullscreen) { if (displayId != mDisplayId) { return; } 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 5e716716301a..d132abe5860f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -2408,6 +2408,11 @@ public class NotificationPanelViewController extends PanelViewController { } @Override + protected void setIsShadeOpening(boolean isOpening) { + mNotificationStackScrollLayoutController.setIsShadeOpening(isOpening); + } + + @Override protected void setOverExpansion(float overExpansion, boolean isPixels) { if (mConflictingQsExpansionGesture || mQsExpandImmediate) { return; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java index c1065189f716..a1112dc37b50 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java @@ -52,6 +52,9 @@ public abstract class PanelBar extends FrameLayout { public void go(int state) { if (DEBUG) LOG("go state: %d -> %d", mState, state); mState = state; + if (mPanel != null) { + mPanel.setIsShadeOpening(state == STATE_OPENING); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 62ded0a2d6e1..da82986fe6b1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -740,6 +740,8 @@ public abstract class PanelViewController { */ protected abstract boolean isTrackingBlocked(); + protected abstract void setIsShadeOpening(boolean isShadeOpening); + protected abstract void setOverExpansion(float overExpansion, boolean isPixels); protected abstract void onHeightUpdated(float expandedHeight); 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 f1b3cc5866a4..fc1811b11d73 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -1005,6 +1005,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } private void updateThemeColors() { + if (mScrimBehind == null) return; int background = Utils.getColorAttr(mScrimBehind.getContext(), android.R.attr.colorBackgroundFloating).getDefaultColor(); int accent = Utils.getColorAccent(mScrimBehind.getContext()).getDefaultColor(); 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 8f2382486abd..8ea173bfdc58 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -112,6 +112,7 @@ import android.view.ThreadedRenderer; import android.view.View; import android.view.ViewGroup; import android.view.WindowInsetsController.Appearance; +import android.view.WindowInsetsController.Behavior; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; @@ -444,9 +445,6 @@ public class StatusBar extends SystemUI implements DemoMode, private boolean mTransientShown; - private boolean mAppFullscreen; - private boolean mAppImmersive; - private final DisplayMetrics mDisplayMetrics; // XXX: gesture research @@ -574,6 +572,8 @@ public class StatusBar extends SystemUI implements DemoMode, private NotificationEntry mDraggedDownEntry; private boolean mLaunchCameraWhenFinishedWaking; private boolean mLaunchCameraOnFinishedGoingToSleep; + private boolean mLaunchEmergencyActionWhenFinishedWaking; + private boolean mLaunchEmergencyActionOnFinishedGoingToSleep; private int mLastCameraLaunchSource; protected PowerManager.WakeLock mGestureWakeLock; private Vibrator mVibrator; @@ -921,10 +921,8 @@ public class StatusBar extends SystemUI implements DemoMode, if (containsType(result.mTransientBarTypes, ITYPE_STATUS_BAR)) { showTransientUnchecked(); } - onSystemBarAppearanceChanged(mDisplayId, result.mAppearance, result.mAppearanceRegions, - result.mNavbarColorManagedByIme); - mAppFullscreen = result.mAppFullscreen; - mAppImmersive = result.mAppImmersive; + onSystemBarAttributesChanged(mDisplayId, result.mAppearance, result.mAppearanceRegions, + result.mNavbarColorManagedByIme, result.mBehavior, result.mAppFullscreen); // StatusBarManagerService has a back up of IME token and it's restored here. setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis, @@ -2345,8 +2343,9 @@ public class StatusBar extends SystemUI implements DemoMode, } @Override - public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance, - AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { + public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + @Behavior int behavior, boolean isFullscreen) { if (displayId != mDisplayId) { return; } @@ -2359,6 +2358,7 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarMode, navbarColorManagedByIme); updateBubblesVisibility(); + mStatusBarStateController.setFullscreenState(isFullscreen); } @Override @@ -2432,16 +2432,6 @@ public class StatusBar extends SystemUI implements DemoMode, } @Override - public void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) { - if (displayId != mDisplayId) { - return; - } - mAppFullscreen = isFullscreen; - mAppImmersive = isImmersive; - mStatusBarStateController.setFullscreenState(isFullscreen, isImmersive); - } - - @Override public void showWirelessChargingAnimation(int batteryLevel) { showChargingAnimation(batteryLevel, UNKNOWN_BATTERY_LEVEL, 0); } @@ -2551,16 +2541,6 @@ public class StatusBar extends SystemUI implements DemoMode, } } - /** Returns whether the top activity is in fullscreen mode. */ - public boolean inFullscreenMode() { - return mAppFullscreen; - } - - /** Returns whether the top activity is in immersive mode. */ - public boolean inImmersiveMode() { - return mAppImmersive; - } - public static String viewInfo(View v) { return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom() + ") " + v.getWidth() + "x" + v.getHeight() + "]"; @@ -3844,6 +3824,14 @@ public class StatusBar extends SystemUI implements DemoMode, // is correct. mHandler.post(() -> onCameraLaunchGestureDetected(mLastCameraLaunchSource)); } + + if (mLaunchEmergencyActionOnFinishedGoingToSleep) { + mLaunchEmergencyActionOnFinishedGoingToSleep = false; + + // This gets executed before we will show Keyguard, so post it in order that the + // state is correct. + mHandler.post(() -> onEmergencyActionLaunchGestureDetected()); + } updateIsKeyguard(); } @@ -3890,6 +3878,13 @@ public class StatusBar extends SystemUI implements DemoMode, false /* animate */, mLastCameraLaunchSource); mLaunchCameraWhenFinishedWaking = false; } + if (mLaunchEmergencyActionWhenFinishedWaking) { + mLaunchEmergencyActionWhenFinishedWaking = false; + Intent emergencyIntent = getEmergencyActionIntent(); + if (emergencyIntent != null) { + mContext.startActivityAsUser(emergencyIntent, UserHandle.CURRENT); + } + } updateScrimController(); } }; @@ -4027,20 +4022,65 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onEmergencyActionLaunchGestureDetected() { - // TODO (b/169793384) Polish the panic gesture to be just like its older brother, camera. + Intent emergencyIntent = getEmergencyActionIntent(); + + if (emergencyIntent == null) { + Log.wtf(TAG, "Couldn't find an app to process the emergency intent."); + return; + } + + if (isGoingToSleep()) { + mLaunchEmergencyActionOnFinishedGoingToSleep = true; + return; + } + + if (!mDeviceInteractive) { + mPowerManager.wakeUp(SystemClock.uptimeMillis(), + PowerManager.WAKE_REASON_GESTURE, + "com.android.systemui:EMERGENCY_GESTURE"); + } + // TODO(b/169087248) Possibly add haptics here for emergency action. Currently disabled for + // app-side haptic experimentation. + + if (!mStatusBarKeyguardViewManager.isShowing()) { + startActivityDismissingKeyguard(emergencyIntent, + false /* onlyProvisioned */, true /* dismissShade */, + true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0); + return; + } + + if (!mDeviceInteractive) { + // Avoid flickering of the scrim when we instant launch the camera and the bouncer + // comes on. + mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L); + } + + if (isWakingUpOrAwake()) { + if (mStatusBarKeyguardViewManager.isBouncerShowing()) { + mStatusBarKeyguardViewManager.reset(true /* hide */); + } + mContext.startActivityAsUser(emergencyIntent, UserHandle.CURRENT); + return; + } + // We need to defer the emergency action launch until the screen comes on, since otherwise + // we will dismiss us too early since we are waiting on an activity to be drawn and + // incorrectly get notified because of the screen on event (which resumes and pauses + // some activities) + mLaunchEmergencyActionWhenFinishedWaking = true; + } + + private @Nullable Intent getEmergencyActionIntent() { Intent emergencyIntent = new Intent(EmergencyGesture.ACTION_LAUNCH_EMERGENCY); PackageManager pm = mContext.getPackageManager(); ResolveInfo resolveInfo = pm.resolveActivity(emergencyIntent, /*flags=*/0); if (resolveInfo == null) { - // TODO(b/171084088) Upgrade log to wtf when we have default app in main branch. - Log.d(TAG, "Couldn't find an app to process the emergency intent."); - return; + Log.wtf(TAG, "Couldn't find an app to process the emergency intent."); + return null; } - emergencyIntent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name)); emergencyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(emergencyIntent, /*dismissShade=*/true); + return emergencyIntent; } boolean isCameraAllowedByAdmin() { @@ -4100,8 +4140,10 @@ public class StatusBar extends SystemUI implements DemoMode, ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming() ? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER; mScrimController.transitionTo(state); - } else if (isInLaunchTransition() || mLaunchCameraWhenFinishedWaking + } else if (isInLaunchTransition() + || mLaunchCameraWhenFinishedWaking || launchingAffordanceWithPreview) { + // TODO(b/170133395) Investigate whether Emergency Gesture flag should be included here. mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback); } else if (mBrightnessMirrorVisible) { mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index f0efed332c7f..00acd7bb6707 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.StatusBarMobileView; import com.android.systemui.statusbar.StatusBarWifiView; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.NoCallingIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import java.util.List; @@ -62,6 +63,10 @@ public interface StatusBarIconController { public void setIcon(String slot, StatusBarIcon icon); public void setSignalIcon(String slot, WifiIconState state); public void setMobileIcons(String slot, List<MobileIconState> states); + /** + * Display the no calling & SMS icons. + */ + void setNoCallingIcons(String slot, List<NoCallingIconState> states); public void setIconVisibility(String slot, boolean b); /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java index 2870152ed853..5e8d59041fab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -35,6 +35,7 @@ import com.android.systemui.demomode.DemoModeController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.NoCallingIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; @@ -216,6 +217,29 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu } } + /** + * Accept a list of NoCallingIconStates, and show them in the same slot + * @param slot StatusBar slot + * @param states All of the no Calling & SMS icon states + */ + @Override + public void setNoCallingIcons(String slot, List<NoCallingIconState> states) { + Slot noCallingSlot = getSlot(slot); + int slotIndex = getSlotIndex(slot); + + for (NoCallingIconState state : states) { + StatusBarIconHolder holder = noCallingSlot.getHolderForTag(state.subId); + if (holder == null) { + holder = StatusBarIconHolder.fromNoCallingState(mContext, state); + holder.setVisible(state.visible); + setIcon(slotIndex, holder); + } else { + holder.setVisible(state.visible); + setIcon(slotIndex, holder); + } + } + } + @Override public void setExternalIcon(String slot) { int viewIndex = getViewIndex(getSlotIndex(slot), 0); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java index 88d0035b333d..36a0e63db19f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java @@ -23,6 +23,7 @@ import android.os.UserHandle; import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.NoCallingIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; /** @@ -70,6 +71,18 @@ public class StatusBarIconHolder { return holder; } + /** + * Creates a new StatusBarIconHolder from a NoCallingIconState. + */ + public static StatusBarIconHolder fromNoCallingState( + Context context, NoCallingIconState state) { + StatusBarIconHolder holder = new StatusBarIconHolder(); + holder.mIcon = new StatusBarIcon(UserHandle.SYSTEM, context.getPackageName(), + Icon.createWithResource(context, state.resId), 0, 0, null); + holder.mTag = state.subId; + return holder; + } + public int getType() { return mType; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java index 1fdd8161914a..d11e8641f987 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -46,6 +46,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba private final String mSlotWifi; private final String mSlotEthernet; private final String mSlotVpn; + private final String mSlotNoCalling; private final Context mContext; private final StatusBarIconController mIconController; @@ -66,6 +67,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba private boolean mWifiVisible = false; private ArrayList<MobileIconState> mMobileStates = new ArrayList<MobileIconState>(); + private ArrayList<NoCallingIconState> mNoCallingStates = new ArrayList<NoCallingIconState>(); private WifiIconState mWifiIconState = new WifiIconState(); public StatusBarSignalPolicy(Context context, StatusBarIconController iconController) { @@ -76,6 +78,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba mSlotWifi = mContext.getString(com.android.internal.R.string.status_bar_wifi); mSlotEthernet = mContext.getString(com.android.internal.R.string.status_bar_ethernet); mSlotVpn = mContext.getString(com.android.internal.R.string.status_bar_vpn); + mSlotNoCalling = mContext.getString(com.android.internal.R.string.status_bar_no_calling); mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity); mIconController = iconController; @@ -198,6 +201,22 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba } @Override + public void setNoCallingStatus(boolean noCalling, int subId) { + if (DEBUG) { + Log.d(TAG, "setNoCallingStatus: " + + "noCalling = " + noCalling + "," + + "subId = " + subId); + } + NoCallingIconState state = getNoCallingState(subId); + if (state == null) { + return; + } + state.visible = noCalling; + mIconController.setNoCallingIcons( + mSlotNoCalling, NoCallingIconState.copyStates(mNoCallingStates)); + } + + @Override public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, int qsType, boolean activityIn, boolean activityOut, CharSequence typeContentDescription, @@ -252,6 +271,16 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba } } + private NoCallingIconState getNoCallingState(int subId) { + for (NoCallingIconState state : mNoCallingStates) { + if (state.subId == subId) { + return state; + } + } + Log.e(TAG, "Unexpected subscription " + subId); + return null; + } + private MobileIconState getState(int subId) { for (MobileIconState state : mMobileStates) { if (state.subId == subId) { @@ -285,9 +314,11 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba mIconController.removeAllIconsForSlot(mSlotMobile); mMobileStates.clear(); + mNoCallingStates.clear(); final int n = subs.size(); for (int i = 0; i < n; i++) { mMobileStates.add(new MobileIconState(subs.get(i).getSubscriptionId())); + mNoCallingStates.add(new NoCallingIconState(subs.get(i).getSubscriptionId())); } } @@ -377,6 +408,53 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba // Don't care. } + /** + * Stores the StatusBar state for no Calling & SMS. + */ + public static class NoCallingIconState { + public boolean visible; + public int resId; + public int subId; + + private NoCallingIconState(int subId) { + this.subId = subId; + this.resId = R.drawable.ic_qs_no_calling_sms; + } + + @Override + public boolean equals(Object o) { + // Skipping reference equality bc this should be more of a value type + if (o == null || getClass() != o.getClass()) { + return false; + } + NoCallingIconState that = (NoCallingIconState) o; + return visible == that.visible + && resId == that.resId + && subId == that.subId; + } + + @Override + public int hashCode() { + return Objects.hash(visible, resId, subId); + } + + private void copyTo(NoCallingIconState other) { + other.visible = visible; + other.resId = resId; + other.subId = subId; + } + + private static List<NoCallingIconState> copyStates(List<NoCallingIconState> inStates) { + ArrayList<NoCallingIconState> outStates = new ArrayList<>(); + for (NoCallingIconState state : inStates) { + NoCallingIconState copy = new NoCallingIconState(state.subId); + state.copyTo(copy); + outStates.add(copy); + } + return outStates; + } + } + private static abstract class SignalIconState { public boolean visible; public boolean activityOut; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java index 1cc312adcbf0..5e88cd5c2423 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java @@ -146,6 +146,15 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa } @Override + public void setNoCallingStatus(boolean noCalling, int subId) { + post(() -> { + for (SignalCallback signalCluster : mSignalCallbacks) { + signalCluster.setNoCallingStatus(noCalling, subId); + } + }); + } + + @Override public void setSubs(List<SubscriptionInfo> subs) { obtainMessage(MSG_SUBS_CHANGED, subs).sendToTarget(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java new file mode 100644 index 000000000000..a76d08a438f2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import android.hardware.SensorPrivacyManager.IndividualSensor; + +public interface IndividualSensorPrivacyController extends + CallbackController<IndividualSensorPrivacyController.Callback> { + void init(); + + boolean isSensorBlocked(@IndividualSensor int sensor); + + void setSensorBlocked(@IndividualSensor int sensor, boolean blocked); + + interface Callback { + void onSensorBlockedChanged(@IndividualSensor int sensor, boolean blocked); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java new file mode 100644 index 000000000000..231fe08e6a99 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA; +import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE; + +import android.hardware.SensorPrivacyManager; +import android.hardware.SensorPrivacyManager.IndividualSensor; +import android.util.ArraySet; +import android.util.SparseBooleanArray; + +import androidx.annotation.NonNull; + +import java.util.Set; + +public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPrivacyController { + + private static final int[] SENSORS = new int[] {CAMERA, MICROPHONE}; + + private final @NonNull SensorPrivacyManager mSensorPrivacyManager; + private final SparseBooleanArray mState = new SparseBooleanArray(); + private final Set<Callback> mCallbacks = new ArraySet<>(); + + public IndividualSensorPrivacyControllerImpl( + @NonNull SensorPrivacyManager sensorPrivacyManager) { + mSensorPrivacyManager = sensorPrivacyManager; + } + + @Override + public void init() { + for (int sensor : SENSORS) { + mSensorPrivacyManager.addSensorPrivacyListener(sensor, + (enabled) -> onSensorPrivacyChanged(sensor, enabled)); + + mState.put(sensor, mSensorPrivacyManager.isIndividualSensorPrivacyEnabled(sensor)); + } + } + + @Override + public boolean isSensorBlocked(@IndividualSensor int sensor) { + return mState.get(sensor, false); + } + + @Override + public void setSensorBlocked(@IndividualSensor int sensor, boolean blocked) { + mSensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, blocked); + } + + @Override + public void addCallback(@NonNull Callback listener) { + mCallbacks.add(listener); + } + + @Override + public void removeCallback(@NonNull Callback listener) { + mCallbacks.remove(listener); + } + + private void onSensorPrivacyChanged(@IndividualSensor int sensor, boolean blocked) { + mState.put(sensor, blocked); + + for (Callback callback : mCallbacks) { + callback.onSensorBlockedChanged(sensor, blocked); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 101e3c6f1bc6..2f66508dafee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -458,9 +458,24 @@ public class MobileSignalController extends SignalController<MobileState, Mobile mCurrentState.dataSim = mobileStatus.dataSim; mCurrentState.carrierNetworkChangeMode = mobileStatus.carrierNetworkChangeMode; mDataState = mobileStatus.dataState; - mServiceState = mobileStatus.serviceState; mSignalStrength = mobileStatus.signalStrength; mTelephonyDisplayInfo = mobileStatus.telephonyDisplayInfo; + int lastVoiceState = mServiceState != null ? mServiceState.getState() : -1; + mServiceState = mobileStatus.serviceState; + int currentVoiceState = mServiceState != null ? mServiceState.getState() : -1; + // Only update the no calling Status in the below scenarios + // 1. The first valid voice state has been received + // 2. The voice state has been changed and either the last or current state is + // ServiceState.STATE_IN_SERVICE + if (mProviderModel + && lastVoiceState != currentVoiceState + && (lastVoiceState == -1 + || (lastVoiceState == ServiceState.STATE_IN_SERVICE + || currentVoiceState == ServiceState.STATE_IN_SERVICE))) { + notifyNoCallingStatusChange( + currentVoiceState != ServiceState.STATE_IN_SERVICE, + mSubscriptionInfo.getSubscriptionId()); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index a9c601601961..f2b0d762b6fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -92,6 +92,13 @@ public interface NetworkController extends CallbackController<SignalCallback>, D */ default void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork, boolean noNetworksAvailable) {} + + /** + * Callback for listeners to be able to update the no calling & SMS status + * @param noCalling whether the calling and SMS is not working. + * @param subId subscription ID for which to update the UI + */ + default void setNoCallingStatus(boolean noCalling, int subId) {} } public interface EmergencyListener { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyController.java index 6d5ce60ef621..4a09234325ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyController.java @@ -23,6 +23,11 @@ public interface SensorPrivacyController extends CallbackController<SensorPrivacyController.OnSensorPrivacyChangedListener> { /** + * Initialize the controller. Needs to be called after constructing the object + */ + void init(); + + /** * Returns whether sensor privacy is enabled. */ boolean isSensorPrivacyEnabled(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java index 20cc46ff6bbd..a2334f3a23d6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java @@ -35,20 +35,21 @@ import javax.inject.Inject; public class SensorPrivacyControllerImpl implements SensorPrivacyController, SensorPrivacyManager.OnSensorPrivacyChangedListener { private SensorPrivacyManager mSensorPrivacyManager; - private final List<OnSensorPrivacyChangedListener> mListeners; + private final List<OnSensorPrivacyChangedListener> mListeners = new ArrayList<>(1); private Object mLock = new Object(); private boolean mSensorPrivacyEnabled; /** * Public constructor. */ - @Inject - public SensorPrivacyControllerImpl(Context context) { - mSensorPrivacyManager = (SensorPrivacyManager) context.getSystemService( - Context.SENSOR_PRIVACY_SERVICE); + public SensorPrivacyControllerImpl(@NonNull SensorPrivacyManager sensorPrivacyManager) { + mSensorPrivacyManager = sensorPrivacyManager; + } + + @Override + public void init() { mSensorPrivacyEnabled = mSensorPrivacyManager.isSensorPrivacyEnabled(); mSensorPrivacyManager.addSensorPrivacyListener(this); - mListeners = new ArrayList<>(1); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java index a05fe1f1b0c2..554145e9773e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java @@ -167,6 +167,10 @@ public abstract class SignalController<T extends State, I extends IconGroup> { } } + protected final void notifyNoCallingStatusChange(boolean noCalling, int subId) { + mCallbackHandler.setNoCallingStatus(noCalling, subId); + } + /** * Returns the resource if resId is not 0, and an empty string otherwise. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt index 6a3a69c0419e..ea803253ea0f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.policy import android.app.Notification +import android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY import android.app.PendingIntent import android.app.RemoteInput import android.content.Context @@ -310,11 +311,19 @@ interface SmartActionInflater { actionIndex: Int, action: Notification.Action ) = + if (smartActions.fromAssistant + && SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY == action.semanticAction) { + entry.row.doSmartActionClick(entry.row.x.toInt() / 2, + entry.row.y.toInt() / 2, SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY) + smartReplyController + .smartActionClicked(entry, actionIndex, action, smartActions.fromAssistant) + } else { activityStarter.startPendingIntentDismissingKeyguard(action.actionIntent, entry.row) { smartReplyController - .smartActionClicked(entry, actionIndex, action, smartActions.fromAssistant) + .smartActionClicked(entry, actionIndex, action, smartActions.fromAssistant) headsUpManager.removeNotification(entry.key, true /* releaseImmediately */) } + } } interface SmartReplyInflater { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java index 069b4051af50..7a4b912d4071 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java @@ -107,11 +107,6 @@ public interface StatusBarPolicyModule { /** */ @Binds - SensorPrivacyController provideSensorPrivacyControllerImpl( - SensorPrivacyControllerImpl controllerImpl); - - /** */ - @Binds UserInfoController provideUserInfoContrller(UserInfoControllerImpl controllerImpl); /** */ diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java index 56a4c203e840..df889f2c2ca6 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java @@ -20,6 +20,7 @@ import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME; import android.content.Context; +import android.hardware.SensorPrivacyManager; import android.os.Handler; import android.os.PowerManager; @@ -63,6 +64,10 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerImpl; +import com.android.systemui.statusbar.policy.SensorPrivacyController; +import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl; import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler; import javax.inject.Named; @@ -109,6 +114,25 @@ public abstract class TvSystemUIModule { return bC; } + @Provides + @SysUISingleton + static SensorPrivacyController provideSensorPrivacyController( + SensorPrivacyManager sensorPrivacyManager) { + SensorPrivacyController spC = new SensorPrivacyControllerImpl(sensorPrivacyManager); + spC.init(); + return spC; + } + + @Provides + @SysUISingleton + static IndividualSensorPrivacyController provideIndividualSensorPrivacyController( + SensorPrivacyManager sensorPrivacyManager) { + IndividualSensorPrivacyController spC = new IndividualSensorPrivacyControllerImpl( + sensorPrivacyManager); + spC.init(); + return spC; + } + @Binds @SysUISingleton abstract QSFactory bindQSFactory(QSFactoryImpl qsFactoryImpl); diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java index 08cd6e383897..8d77c4a194a9 100644 --- a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java +++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java @@ -33,7 +33,7 @@ public interface WakeLock { static final String REASON_WRAP = "wrap"; /** - * Default wake-lock timeout, to avoid battery regressions. + * Default wake-lock timeout in milliseconds, to avoid battery regressions. */ long DEFAULT_MAX_TIMEOUT = 20000; @@ -104,6 +104,7 @@ public interface WakeLock { if (count == null) { Log.wtf(TAG, "Releasing WakeLock with invalid reason: " + why, new Throwable()); + return; } else if (count == 1) { mActiveClients.remove(why); } else { diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 715b0a25e461..81ac21c00823 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -55,14 +55,14 @@ import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.tracing.nano.SystemUiTraceProto; import com.android.wm.shell.ShellCommandHandler; 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.OneHandedEvents; import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; +import com.android.wm.shell.onehanded.OneHandedUiEventLogger; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.protolog.ShellProtoLogImpl; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -253,7 +253,7 @@ public final class WMShell extends SystemUI mSysUiMainExecutor.execute(() -> { if (oneHanded.isOneHandedEnabled()) { oneHanded.stopOneHanded( - OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT); + OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT); } else if (oneHanded.isSwipeToNotificationEnabled()) { mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP); } @@ -280,7 +280,7 @@ public final class WMShell extends SystemUI @Override public void onScreenTurningOff() { oneHanded.stopOneHanded( - OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_SCREEN_OFF_OUT); + OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_SCREEN_OFF_OUT); } }); @@ -294,7 +294,8 @@ public final class WMShell extends SystemUI public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher) { if (displayId == DEFAULT_DISPLAY && (vis & InputMethodService.IME_VISIBLE) != 0) { - oneHanded.stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_POP_IME_OUT); + oneHanded.stopOneHanded( + OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_POP_IME_OUT); } } }); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index 1b5877f9da66..bbc238a35cbd 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -350,10 +350,11 @@ public abstract class WMShellBaseModule { @Provides static Optional<OneHanded> provideOneHandedController(Context context, DisplayController displayController, TaskStackListenerImpl taskStackListener, + UiEventLogger uiEventLogger, @ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler) { return Optional.ofNullable(OneHandedController.create(context, displayController, - taskStackListener, mainExecutor, mainHandler)); + taskStackListener, uiEventLogger, mainExecutor, mainHandler)); } @WMSingleton diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index b24f4ab9880d..c052563b36a5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -106,6 +106,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor; private IUdfpsOverlayController mOverlayController; @Captor private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor; + @Captor private ArgumentCaptor<Runnable> mRunAfterShowingScrimAndDotCaptor; @Before public void setUp() { @@ -190,11 +191,14 @@ public class UdfpsControllerTest extends SysuiTestCase { MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); event.recycle(); - // THEN the event is passed to the FingerprintManager + // THEN the scrim and dot is shown + verify(mUdfpsView).showScrimAndDot(); + // AND a runnable that passes the event to FingerprintManager is set on the view + verify(mUdfpsView).setRunAfterShowingScrimAndDot( + mRunAfterShowingScrimAndDotCaptor.capture()); + mRunAfterShowingScrimAndDotCaptor.getValue().run(); verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0), eq(0), eq(0f), eq(0f)); - // AND the scrim and dot is shown - verify(mUdfpsView).showScrimAndDot(); } @Test @@ -205,11 +209,14 @@ public class UdfpsControllerTest extends SysuiTestCase { mFgExecutor.runAllReady(); // WHEN fingerprint is requested because of AOD interrupt mUdfpsController.onAodInterrupt(0, 0, 2f, 3f); - // THEN the event is passed to the FingerprintManager + // THEN the scrim and dot is shown + verify(mUdfpsView).showScrimAndDot(); + // AND a runnable that passes the event to FingerprintManager is set on the view + verify(mUdfpsView).setRunAfterShowingScrimAndDot( + mRunAfterShowingScrimAndDotCaptor.capture()); + mRunAfterShowingScrimAndDotCaptor.getValue().run(); verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0), eq(0), eq(3f) /* minor */, eq(2f) /* major */); - // AND the scrim and dot is shown - verify(mUdfpsView).showScrimAndDot(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java index 4d32a3b4077f..b63274ba246a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java @@ -42,6 +42,7 @@ import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.pip.Pip; +import com.android.wm.shell.transition.Transitions; import org.junit.Before; import org.junit.Test; @@ -75,6 +76,7 @@ public class OverviewProxyServiceTest extends SysuiTestCase { @Mock private Optional<com.android.wm.shell.onehanded.OneHanded> mMockOneHandedOptional; @Mock private PackageManager mPackageManager; @Mock private SysUiState mMockSysUiState; + @Mock private Transitions mMockTransitions; @Before public void setUp() throws RemoteException { @@ -89,7 +91,7 @@ public class OverviewProxyServiceTest extends SysuiTestCase { mMockNavBarControllerLazy, mMockNavModeController, mMockStatusBarWinController, mMockSysUiState, mMockPipOptional, mMockSplitScreenOptional, mMockStatusBarOptionalLazy, mMockOneHandedOptional, - mMockBroadcastDispatcher)); + mMockBroadcastDispatcher, mMockTransitions)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java index ced8428e6e6b..03f93fa12451 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java @@ -43,7 +43,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; -import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ShareTransition; +import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; import org.junit.Before; import org.junit.Test; @@ -177,10 +177,10 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { data.mActionsReadyListener = null; SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data, - ShareTransition::new); + ActionTransition::new); Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(), - Uri.parse("Screenshot_123.png")).get().shareAction; + Uri.parse("Screenshot_123.png")).get().action; Intent intent = shareAction.actionIntent.getIntent(); assertNotNull(intent); @@ -205,10 +205,10 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { data.mActionsReadyListener = null; SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data, - ShareTransition::new); + ActionTransition::new); Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(), - Uri.parse("Screenshot_123.png")); + Uri.parse("Screenshot_123.png")).get().action; Intent intent = editAction.actionIntent.getIntent(); assertNotNull(intent); @@ -233,7 +233,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { data.mActionsReadyListener = null; SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data, - ShareTransition::new); + ActionTransition::new); Notification.Action deleteAction = task.createDeleteAction(mContext, mContext.getResources(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java index d2d57087485c..2917dfafd6a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; +import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Matchers.eq; @@ -30,6 +31,7 @@ import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.os.Bundle; import android.view.WindowInsetsController.Appearance; +import android.view.WindowInsetsController.Behavior; import androidx.test.filters.SmallTest; @@ -116,24 +118,27 @@ public class CommandQueueTest extends SysuiTestCase { } @Test - public void testOnSystemBarAppearanceChanged() { - doTestOnSystemBarAppearanceChanged(DEFAULT_DISPLAY, 1, - new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false); + public void testOnSystemBarAttributesChanged() { + doTestOnSystemBarAttributesChanged(DEFAULT_DISPLAY, 1, + new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false, + BEHAVIOR_DEFAULT, false); } @Test - public void testOnSystemBarAppearanceChangedForSecondaryDisplay() { - doTestOnSystemBarAppearanceChanged(SECONDARY_DISPLAY, 1, - new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false); + public void testOnSystemBarAttributesChangedForSecondaryDisplay() { + doTestOnSystemBarAttributesChanged(SECONDARY_DISPLAY, 1, + new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false, + BEHAVIOR_DEFAULT, false); } - private void doTestOnSystemBarAppearanceChanged(int displayId, @Appearance int appearance, - AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { - mCommandQueue.onSystemBarAppearanceChanged(displayId, appearance, appearanceRegions, - navbarColorManagedByIme); + private void doTestOnSystemBarAttributesChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + @Behavior int behavior, boolean isFullscreen) { + mCommandQueue.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions, + navbarColorManagedByIme, behavior, isFullscreen); waitForIdleSync(); - verify(mCallbacks).onSystemBarAppearanceChanged(eq(displayId), eq(appearance), - eq(appearanceRegions), eq(navbarColorManagedByIme)); + verify(mCallbacks).onSystemBarAttributesChanged(eq(displayId), eq(appearance), + eq(appearanceRegions), eq(navbarColorManagedByIme), eq(behavior), eq(isFullscreen)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java index 291b223d72bb..5c37656d2cf1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java @@ -241,6 +241,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SetsShortcutIcon() { mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -265,6 +266,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { public void testBindNotification_SetsTextApplicationName() { when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -289,6 +291,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SetsTextChannelName() { mNotificationInfo.bindNotification( + -1, mShortcutManager, mLauncherApps, mMockPackageManager, @@ -316,6 +319,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mConversationChannel.setGroup(group.getId()); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -341,6 +345,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { @Test public void testBindNotification_GroupNameHiddenIfNoGroup() { mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -365,6 +370,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { @Test public void testBindNotification_noDelegate() { mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -400,6 +406,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { .setShortcutInfo(mShortcutInfo) .build(); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -425,6 +432,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { public void testBindNotification_SetsOnClickListenerForSettings() { final CountDownLatch latch = new CountDownLatch(1); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -454,6 +462,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() { mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -478,6 +487,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { public void testBindNotification_SettingsButtonInvisibleWhenDeviceUnprovisioned() { final CountDownLatch latch = new CountDownLatch(1); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -506,6 +516,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mConversationChannel.setImportance(IMPORTANCE_LOW); mConversationChannel.setImportantConversation(true); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -534,6 +545,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mConversationChannel.setImportantConversation(false); mConversationChannel.setAllowBubbles(true); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -565,6 +577,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mConversationChannel.setImportantConversation(false); mConversationChannel.setAllowBubbles(true); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -595,6 +608,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mConversationChannel.setImportantConversation(false); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -639,6 +653,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mConversationChannel.setImportance(IMPORTANCE_LOW); mConversationChannel.setImportantConversation(false); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -682,6 +697,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mConversationChannel.setImportantConversation(false); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -726,6 +742,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mConversationChannel.setImportantConversation(false); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -763,6 +780,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mConversationChannel.setImportance(9); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -799,6 +817,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mConversationChannel.setImportantConversation(true); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -832,11 +851,75 @@ public class NotificationConversationInfoTest extends SysuiTestCase { } @Test + public void testDefaultSelectedWhenChannelIsDefault() throws Exception { + // GIVEN channel importance indicates "Default" priority + mConversationChannel.setImportance(IMPORTANCE_HIGH); + mConversationChannel.setImportantConversation(false); + + // WHEN we indicate no selected action + mNotificationInfo.bindNotification( + -1, // no action selected by default + mShortcutManager, + mMockPackageManager, + mMockINotificationManager, + mOnUserInteractionCallback, + TEST_PACKAGE_NAME, + mNotificationChannel, + mEntry, + mBubbleMetadata, + null, + null, + mIconFactory, + mContext, + mBuilderProvider, + true, + mTestHandler, + mTestHandler, null, Optional.of(mBubblesManager)); + + // THEN the selected action is -1, so the selected option is "Default" priority + assertEquals(mNotificationInfo.getSelectedAction(), -1); + assertTrue(mNotificationInfo.findViewById(R.id.default_behavior).isSelected()); + } + + @Test + public void testFavoriteSelectedWhenChannelIsDefault() throws Exception { + // GIVEN channel importance indicates "Default" priority + mConversationChannel.setImportance(IMPORTANCE_HIGH); + mConversationChannel.setImportantConversation(false); + + // WHEN we indicate the selected action should be "Favorite" + mNotificationInfo.bindNotification( + NotificationConversationInfo.ACTION_FAVORITE, // "Favorite" selected by default + mShortcutManager, + mMockPackageManager, + mMockINotificationManager, + mOnUserInteractionCallback, + TEST_PACKAGE_NAME, + mNotificationChannel, + mEntry, + mBubbleMetadata, + null, + null, + mIconFactory, + mContext, + mBuilderProvider, + true, + mTestHandler, + mTestHandler, null, Optional.of(mBubblesManager)); + + // THEN the selected action is "Favorite", so the selected option is "priority" priority + assertEquals(mNotificationInfo.getSelectedAction(), + NotificationConversationInfo.ACTION_FAVORITE); + assertTrue(mNotificationInfo.findViewById(R.id.priority).isSelected()); + } + + @Test public void testDefault_andSave() throws Exception { mConversationChannel.setAllowBubbles(true); mConversationChannel.setOriginalImportance(IMPORTANCE_HIGH); mConversationChannel.setImportantConversation(true); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -873,6 +956,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mConversationChannel.setOriginalImportance(IMPORTANCE_HIGH); mConversationChannel.setImportantConversation(false); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -909,6 +993,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mConversationChannel.setOriginalImportance(IMPORTANCE_HIGH); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -944,6 +1029,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mConversationChannel.setAllowBubbles(true); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -978,6 +1064,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { @Test public void testBindNotification_createsNewChannel() throws Exception { mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -1003,6 +1090,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { public void testBindNotification_doesNotCreateNewChannelIfExists() throws Exception { mNotificationChannel.setConversationId("", CONVERSATION_ID); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -1038,6 +1126,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { // GIVEN the user is changing conversation settings mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, @@ -1078,6 +1167,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { when(b.build()).thenReturn(controller); mNotificationInfo.bindNotification( + -1, mShortcutManager, mMockPackageManager, mMockINotificationManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java index dbb451277535..cdfab1eec609 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone; import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL; import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; +import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -95,21 +96,25 @@ public class LightsOutNotifControllerTest extends SysuiTestCase { @Test public void testAreLightsOut_lightsOut() { - mCallbacks.onSystemBarAppearanceChanged( + mCallbacks.onSystemBarAttributesChanged( mDisplayId /* display id */, LIGHTS_OUT /* appearance */, null /* appearanceRegions */, - false /* navbarColorManagedByIme */); + false /* navbarColorManagedByIme */, + BEHAVIOR_DEFAULT, + false /* isFullscreen */); assertTrue(mLightsOutNotifController.areLightsOut()); } @Test public void testAreLightsOut_lightsOn() { - mCallbacks.onSystemBarAppearanceChanged( + mCallbacks.onSystemBarAttributesChanged( mDisplayId /* display id */, LIGHTS_ON /* appearance */, null /* appearanceRegions */, - false /* navbarColorManagedByIme */); + false /* navbarColorManagedByIme */, + BEHAVIOR_DEFAULT, + false /* isFullscreen */); assertFalse(mLightsOutNotifController.areLightsOut()); } @@ -128,16 +133,18 @@ public class LightsOutNotifControllerTest extends SysuiTestCase { } @Test - public void testLightsOut_withNotifs_onSystemBarAppearanceChanged() { + public void testLightsOut_withNotifs_onSystemBarAttributesChanged() { // GIVEN active visible notifications when(mEntryManager.hasActiveNotifications()).thenReturn(true); // WHEN lights out - mCallbacks.onSystemBarAppearanceChanged( + mCallbacks.onSystemBarAttributesChanged( mDisplayId /* display id */, LIGHTS_OUT /* appearance */, null /* appearanceRegions */, - false /* navbarColorManagedByIme */); + false /* navbarColorManagedByIme */, + BEHAVIOR_DEFAULT, + false /* isFullscreen */); // THEN we should show dot assertTrue(mLightsOutNotifController.shouldShowDot()); @@ -145,16 +152,18 @@ public class LightsOutNotifControllerTest extends SysuiTestCase { } @Test - public void testLightsOut_withoutNotifs_onSystemBarAppearanceChanged() { + public void testLightsOut_withoutNotifs_onSystemBarAttributesChanged() { // GIVEN no active visible notifications when(mEntryManager.hasActiveNotifications()).thenReturn(false); // WHEN lights out - mCallbacks.onSystemBarAppearanceChanged( + mCallbacks.onSystemBarAttributesChanged( mDisplayId /* display id */, LIGHTS_OUT /* appearance */, null /* appearanceRegions */, - false /* navbarColorManagedByIme */); + false /* navbarColorManagedByIme */, + BEHAVIOR_DEFAULT, + false /* isFullscreen */); // THEN we shouldn't show the dot assertFalse(mLightsOutNotifController.shouldShowDot()); @@ -162,16 +171,18 @@ public class LightsOutNotifControllerTest extends SysuiTestCase { } @Test - public void testLightsOn_afterLightsOut_onSystemBarAppearanceChanged() { + public void testLightsOn_afterLightsOut_onSystemBarAttributesChanged() { // GIVEN active visible notifications when(mEntryManager.hasActiveNotifications()).thenReturn(true); // WHEN lights on - mCallbacks.onSystemBarAppearanceChanged( + mCallbacks.onSystemBarAttributesChanged( mDisplayId /* display id */, LIGHTS_ON /* appearance */, null /* appearanceRegions */, - false /* navbarColorManagedByIme */); + false /* navbarColorManagedByIme */, + BEHAVIOR_DEFAULT, + false /* isFullscreen */); // THEN we shouldn't show the dot assertFalse(mLightsOutNotifController.shouldShowDot()); 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 8e84f1a4e843..51ce8e59e999 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 @@ -24,7 +24,6 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.fail; -import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -35,7 +34,6 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -45,7 +43,6 @@ import android.app.StatusBarManager; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; -import android.content.Intent; import android.content.IntentFilter; import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.fingerprint.FingerprintManager; @@ -87,7 +84,6 @@ import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.demomode.DemoModeController; -import com.android.systemui.emergency.EmergencyGesture; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; @@ -151,10 +147,8 @@ import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -887,19 +881,6 @@ public class StatusBarTest extends SysuiTestCase { verify(mDozeServiceHost).setDozeSuppressed(false); } - @Ignore // TODO (b/175240607) - Figure out if the device will actually dial 911. - @Test - public void onEmergencyActionLaunchGesture_launchesEmergencyIntent() { - ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); - StatusBar statusBarSpy = spy(mStatusBar); - - statusBarSpy.onEmergencyActionLaunchGestureDetected(); - - verify(statusBarSpy).startActivity(intentCaptor.capture(), eq(true)); - Intent sentIntent = intentCaptor.getValue(); - assertEquals(sentIntent.getAction(), EmergencyGesture.ACTION_LAUNCH_EMERGENCY); - } - public static class TestableNotificationInterruptStateProviderImpl extends NotificationInterruptStateProviderImpl { diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java index 3357be8c8b84..fe01f841aa16 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java @@ -19,6 +19,7 @@ package com.android.systemui.util.wakelock; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.os.Build; import android.os.PowerManager; import androidx.test.filters.SmallTest; @@ -85,4 +86,14 @@ public class WakeLockTest extends SysuiTestCase { assertTrue(ran[0]); assertFalse(mInner.isHeld()); } + + @Test + public void prodBuild_wakeLock_releaseWithoutAcquire_noThrow() { + if (Build.IS_ENG) { + return; + } + + // shouldn't throw an exception on production builds + mWakeLock.release(WHY); + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java index 2e874a6c2140..c0af15b1f96d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java @@ -20,6 +20,7 @@ import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.NoCallingIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import java.util.List; @@ -65,6 +66,10 @@ public class FakeStatusBarIconController extends BaseLeakChecker<IconManager> } @Override + public void setNoCallingIcons(String slot, List<NoCallingIconState> states) { + } + + @Override public void setIconVisibility(String slotTty, boolean b) { } 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 446d3f2f72a0..8b86403b554e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -120,4 +120,4 @@ public class WMShellTest extends SysuiTestCase { verify(mConfigurationController).addCallback( any(ConfigurationController.ConfigurationListener.class)); } -}
\ No newline at end of file +} diff --git a/packages/services/CameraExtensionsProxy/OWNERS b/packages/services/CameraExtensionsProxy/OWNERS new file mode 100644 index 000000000000..f48a95c5b3a3 --- /dev/null +++ b/packages/services/CameraExtensionsProxy/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/av:/camera/OWNERS diff --git a/services/accessibility/OWNERS b/services/accessibility/OWNERS index c6f42f719caa..a31cfae995b2 100644 --- a/services/accessibility/OWNERS +++ b/services/accessibility/OWNERS @@ -1,4 +1,4 @@ svetoslavganov@google.com pweaver@google.com rhedjao@google.com -qasid@google.com +ryanlwlin@google.com diff --git a/services/core/java/android/power/PowerStatsInternal.java b/services/core/java/android/power/PowerStatsInternal.java new file mode 100644 index 000000000000..9c908c386efd --- /dev/null +++ b/services/core/java/android/power/PowerStatsInternal.java @@ -0,0 +1,42 @@ +/* + * 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.power; + +import android.hardware.power.stats.EnergyConsumerId; +import android.hardware.power.stats.EnergyConsumerResult; + +import java.util.concurrent.CompletableFuture; + +/** + * Power stats local system service interface. + * + * @hide Only for use within Android OS. + */ +public abstract class PowerStatsInternal { + /** + * Returns a CompletableFuture that will get an {@link EnergyConsumerResult} array for the + * available requested energy consumers (power models). + * + * @param energyConsumerIds Array of {@link EnergyConsumerId} for which energy consumed is being + * requested. + * + * @return A Future containing a list of {@link EnergyConsumerResult} objects containing energy + * consumer results for all listed {@link EnergyConsumerId}. + */ + public abstract CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync( + @EnergyConsumerId int[] energyConsumerIds); +} diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 6b45abde74b4..7541833b1569 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -194,7 +194,6 @@ import com.android.server.connectivity.AutodestructReference; import com.android.server.connectivity.DataConnectionStats; import com.android.server.connectivity.DnsManager; import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate; -import com.android.server.connectivity.IpConnectivityMetrics; import com.android.server.connectivity.KeepaliveTracker; import com.android.server.connectivity.LingerMonitor; import com.android.server.connectivity.MockableSystemProperties; @@ -889,6 +888,13 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** + * Get a reference to the system keystore. + */ + public KeyStore getKeyStore() { + return KeyStore.getInstance(); + } + + /** * @see ProxyTracker */ public ProxyTracker makeProxyTracker(@NonNull Context context, @@ -918,14 +924,6 @@ public class ConnectivityService extends IConnectivityManager.Stub return new MultinetworkPolicyTracker(c, h, r); } - /** - * @see IpConnectivityMetrics.Logger - */ - public IpConnectivityMetrics.Logger getMetricsLogger() { - return Objects.requireNonNull(LocalServices.getService(IpConnectivityMetrics.Logger.class), - "no IpConnectivityMetrics service"); - } - public IBatteryStats getBatteryStatsService() { return BatteryStatsService.getService(); } @@ -990,7 +988,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mProxyTracker = mDeps.makeProxyTracker(mContext, mHandler); mNetd = netd; - mKeyStore = KeyStore.getInstance(); + mKeyStore = mDeps.getKeyStore(); mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); mLocationPermissionChecker = new LocationPermissionChecker(mContext); @@ -1653,7 +1651,6 @@ public class ConnectivityService extends IConnectivityManager.Stub private NetworkCapabilities getNetworkCapabilitiesInternal(NetworkAgentInfo nai) { if (nai == null) return null; synchronized (nai) { - if (nai.networkCapabilities == null) return null; return networkCapabilitiesRestrictedForCallerPermissions( nai.networkCapabilities, Binder.getCallingPid(), mDeps.getCallingUid()); } @@ -2777,7 +2774,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } private boolean isLiveNetworkAgent(NetworkAgentInfo nai, int what) { - if (nai.network == null) return false; final NetworkAgentInfo officialNai = getNetworkAgentInfoForNetwork(nai.network); if (officialNai != null && officialNai.equals(nai)) return true; if (officialNai != null || VDBG) { @@ -3470,6 +3466,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // available until we've told netd to delete it below. mNetworkForNetId.remove(nai.network.getNetId()); } + propagateUnderlyingNetworkCapabilities(nai.network); // Remove all previously satisfied requests. for (int i = 0; i < nai.numNetworkRequests(); i++) { NetworkRequest request = nai.requestAt(i); @@ -3482,7 +3479,9 @@ public class ConnectivityService extends IConnectivityManager.Stub } } nai.clearLingerState(); - propagateUnderlyingNetworkCapabilities(nai.network); + // TODO: this loop, and the mLegacyTypeTracker.remove just below it, seem redundant given + // there's a full rematch right after. Currently, deleting it breaks tests that check for + // the default network disconnecting. Find out why, fix the rematch code, and delete this. if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) { mDefaultNetworkNai = null; updateDataActivityTracking(null /* newNetwork */, nai); @@ -4993,16 +4992,23 @@ public class ConnectivityService extends IConnectivityManager.Stub mVpnBlockedUidRanges = newVpnBlockedUidRanges; } + private boolean isLockdownVpnEnabled() { + return mKeyStore.contains(Credentials.LOCKDOWN_VPN); + } + @Override public boolean updateLockdownVpn() { - if (mDeps.getCallingUid() != Process.SYSTEM_UID) { - logw("Lockdown VPN only available to AID_SYSTEM"); + // Allow the system UID for the system server and for Settings. + // Also, for unit tests, allow the process that ConnectivityService is running in. + if (mDeps.getCallingUid() != Process.SYSTEM_UID + && Binder.getCallingPid() != Process.myPid()) { + logw("Lockdown VPN only available to system process or AID_SYSTEM"); return false; } synchronized (mVpns) { // Tear down existing lockdown if profile was removed - mLockdownEnabled = LockdownVpnTracker.isEnabled(); + mLockdownEnabled = isLockdownVpnEnabled(); if (mLockdownEnabled) { byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN); if (profileTag == null) { @@ -5023,7 +5029,8 @@ public class ConnectivityService extends IConnectivityManager.Stub logw("VPN for user " + user + " not ready yet. Skipping lockdown"); return false; } - setLockdownTracker(new LockdownVpnTracker(mContext, this, mHandler, vpn, profile)); + setLockdownTracker( + new LockdownVpnTracker(mContext, this, mHandler, mKeyStore, vpn, profile)); } else { setLockdownTracker(null); } @@ -5111,7 +5118,7 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized (mVpns) { // Can't set always-on VPN if legacy VPN is already in lockdown mode. - if (LockdownVpnTracker.isEnabled()) { + if (isLockdownVpnEnabled()) { return false; } @@ -5217,7 +5224,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, mNetd, userId, mKeyStore); mVpns.put(userId, userVpn); - if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) { + if (mUserManager.getUserInfo(userId).isPrimary() && isLockdownVpnEnabled()) { updateLockdownVpn(); } } @@ -5301,7 +5308,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private void onUserUnlocked(int userId) { synchronized (mVpns) { // User present may be sent because of an unlock, which might mean an unlocked keystore. - if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) { + if (mUserManager.getUserInfo(userId).isPrimary() && isLockdownVpnEnabled()) { updateLockdownVpn(); } else { startAlwaysOnVpn(userId); @@ -6068,6 +6075,10 @@ public class ConnectivityService extends IConnectivityManager.Stub public Network registerNetworkAgent(INetworkAgent na, NetworkInfo networkInfo, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) { + Objects.requireNonNull(networkInfo, "networkInfo must not be null"); + Objects.requireNonNull(linkProperties, "linkProperties must not be null"); + Objects.requireNonNull(networkCapabilities, "networkCapabilities must not be null"); + Objects.requireNonNull(networkAgentConfig, "networkAgentConfig must not be null"); if (networkCapabilities.hasTransport(TRANSPORT_TEST)) { enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS); } else { @@ -6606,7 +6617,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } // Don't modify caller's NetworkCapabilities. - NetworkCapabilities newNc = new NetworkCapabilities(nc); + final NetworkCapabilities newNc = new NetworkCapabilities(nc); if (nai.lastValidated) { newNc.addCapability(NET_CAPABILITY_VALIDATED); } else { @@ -6694,26 +6705,21 @@ public class ConnectivityService extends IConnectivityManager.Stub notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED); } - // TODO : static analysis indicates that prevNc can't be null here (getAndSetNetworkCaps - // never returns null), so mark the relevant members and functions in nai as @NonNull and - // remove this test - if (prevNc != null) { - final boolean oldMetered = prevNc.isMetered(); - final boolean newMetered = newNc.isMetered(); - final boolean meteredChanged = oldMetered != newMetered; + final boolean oldMetered = prevNc.isMetered(); + final boolean newMetered = newNc.isMetered(); + final boolean meteredChanged = oldMetered != newMetered; - if (meteredChanged) { - maybeNotifyNetworkBlocked(nai, oldMetered, newMetered, mRestrictBackground, - mRestrictBackground, mVpnBlockedUidRanges, mVpnBlockedUidRanges); - } + if (meteredChanged) { + maybeNotifyNetworkBlocked(nai, oldMetered, newMetered, mRestrictBackground, + mRestrictBackground, mVpnBlockedUidRanges, mVpnBlockedUidRanges); + } - final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) != - newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING); + final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) + != newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING); - // Report changes that are interesting for network statistics tracking. - if (meteredChanged || roamingChanged) { - notifyIfacesChangedForNetworkStats(); - } + // Report changes that are interesting for network statistics tracking. + if (meteredChanged || roamingChanged) { + notifyIfacesChangedForNetworkStats(); } // This network might have been underlying another network. Propagate its capabilities. @@ -7588,10 +7594,6 @@ public class ConnectivityService extends IConnectivityManager.Stub if (!networkAgent.everConnected && state == NetworkInfo.State.CONNECTED) { networkAgent.everConnected = true; - if (networkAgent.linkProperties == null) { - Log.wtf(TAG, networkAgent.toShortString() + " connected with null LinkProperties"); - } - // NetworkCapabilities need to be set before sending the private DNS config to // NetworkMonitor, otherwise NetworkMonitor cannot determine if validation is required. networkAgent.getAndSetNetworkCapabilities(networkAgent.networkCapabilities); diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java index a6d9bf8bc55b..f04af8bbf1a0 100644 --- a/services/core/java/com/android/server/DropBoxManagerService.java +++ b/services/core/java/com/android/server/DropBoxManagerService.java @@ -73,7 +73,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.SortedSet; @@ -446,7 +445,10 @@ public final class DropBoxManagerService extends SystemService { // from an in-memory buffer, or another file on disk; if we buffered // we'd lose out on sendfile() optimizations if (forceCompress) { - FileUtils.copy(in, new GZIPOutputStream(new FileOutputStream(fd))); + final GZIPOutputStream gzipOutputStream = + new GZIPOutputStream(new FileOutputStream(fd)); + FileUtils.copy(in, gzipOutputStream); + gzipOutputStream.finish(); } else { FileUtils.copy(in, new FileOutputStream(fd)); } diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS index dfcc32505b9b..b14ce1cc412a 100644 --- a/services/core/java/com/android/server/OWNERS +++ b/services/core/java/com/android/server/OWNERS @@ -5,7 +5,7 @@ per-file ConnectivityService.java,ConnectivityServiceInitializer.java,NetworkMan per-file VibratorManagerService.java, VibratorService.java, DisplayThread.java = michaelwr@google.com, ogunwale@google.com # Zram writeback -per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com, srnvs@google.com +per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com # Userspace reboot per-file UserspaceRebootLogger.java = ioffe@google.com, tomcherry@google.com @@ -30,6 +30,7 @@ per-file IpSecService.java = file:/services/core/java/com/android/server/net/OWN per-file MmsServiceBroker.java = file:/telephony/OWNERS per-file NetIdManager.java = file:/services/core/java/com/android/server/net/OWNERS per-file PackageWatchdog.java = file:/services/core/java/com/android/server/rollback/OWNERS +per-file PinnerService.java = file:/apct-tests/perftests/OWNERS per-file TelephonyRegistry.java = file:/telephony/OWNERS per-file UiModeManagerService.java = file:/packages/SystemUI/OWNERS per-file VcnManagementService.java = file:/services/core/java/com/android/server/vcn/OWNERS diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java index a3bcbbe25e88..871de0dc28bf 100644 --- a/services/core/java/com/android/server/PinnerService.java +++ b/services/core/java/com/android/server/PinnerService.java @@ -43,6 +43,8 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.ResultReceiver; +import android.os.ShellCallback; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; @@ -61,7 +63,6 @@ import com.android.internal.app.ResolverActivity; import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; import com.android.internal.util.function.pooled.PooledLambda; -import com.android.server.SystemService.TargetUser; import com.android.server.wm.ActivityTaskManagerInternal; import dalvik.system.DexFile; @@ -70,6 +71,7 @@ import dalvik.system.VMRuntime; import java.io.Closeable; import java.io.DataInputStream; import java.io.FileDescriptor; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; @@ -100,12 +102,6 @@ public final class PinnerService extends SystemService { private static final int KEY_HOME = 1; private static final int KEY_ASSISTANT = 2; - // Pin the camera application. Default to the system property only if the experiment phenotype - // property is not set. - private static boolean PROP_PIN_CAMERA = - DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT, - "pin_camera", - SystemProperties.getBoolean("pinner.pin_camera", false)); // Pin using pinlist.meta when pinning apps. private static boolean PROP_PIN_PINLIST = SystemProperties.getBoolean( "pinner.use_pinlist", true); @@ -150,7 +146,13 @@ public final class PinnerService extends SystemService { /** * A set of {@link AppKey} that are configured to be pinned. */ - private final ArraySet<Integer> mPinKeys = new ArraySet<>(); + @GuardedBy("this") + private ArraySet<Integer> mPinKeys; + + // Resource-configured pinner flags; + private final boolean mConfiguredToPinCamera; + private final boolean mConfiguredToPinHome; + private final boolean mConfiguredToPinAssistant; private BinderService mBinderService; private PinnerHandler mPinnerHandler = null; @@ -173,25 +175,13 @@ public final class PinnerService extends SystemService { super(context); mContext = context; - boolean shouldPinCamera = context.getResources().getBoolean( + mConfiguredToPinCamera = context.getResources().getBoolean( com.android.internal.R.bool.config_pinnerCameraApp); - boolean shouldPinHome = context.getResources().getBoolean( + mConfiguredToPinHome = context.getResources().getBoolean( com.android.internal.R.bool.config_pinnerHomeApp); - boolean shouldPinAssistant = context.getResources().getBoolean( + mConfiguredToPinAssistant = context.getResources().getBoolean( com.android.internal.R.bool.config_pinnerAssistantApp); - if (shouldPinCamera) { - if (PROP_PIN_CAMERA) { - mPinKeys.add(KEY_CAMERA); - } else if (DEBUG) { - Slog.i(TAG, "Pinner - skip pinning camera app"); - } - } - if (shouldPinHome) { - mPinKeys.add(KEY_HOME); - } - if (shouldPinAssistant) { - mPinKeys.add(KEY_ASSISTANT); - } + mPinKeys = createPinKeys(); mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper()); mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class); @@ -259,9 +249,10 @@ public final class PinnerService extends SystemService { * The other files pinned in onStart will not need to be updated. */ public void update(ArraySet<String> updatedPackages, boolean force) { + ArraySet<Integer> pinKeys = getPinKeys(); int currentUser = ActivityManager.getCurrentUser(); - for (int i = mPinKeys.size() - 1; i >= 0; i--) { - int key = mPinKeys.valueAt(i); + for (int i = pinKeys.size() - 1; i >= 0; i--) { + int key = pinKeys.valueAt(i); ApplicationInfo info = getInfoForKey(key, currentUser); if (info != null && updatedPackages.contains(info.packageName)) { Slog.i(TAG, "Updating pinned files for " + info.packageName + " force=" + force); @@ -385,6 +376,14 @@ public final class PinnerService extends SystemService { } } + private void unpinApps() { + ArraySet<Integer> pinKeys = getPinKeys(); + for (int i = pinKeys.size() - 1; i >= 0; i--) { + int key = pinKeys.valueAt(i); + unpinApp(key); + } + } + private void unpinApp(@AppKey int key) { ArrayList<PinnedFile> pinnedAppFiles; synchronized (this) { @@ -490,9 +489,79 @@ public final class PinnerService extends SystemService { userHandle)); } + private void sendPinAppsWithUpdatedKeysMessage(int userHandle) { + mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinAppsWithUpdatedKeys, + this, userHandle)); + } + private void sendUnpinAppsMessage() { + mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::unpinApps, this)); + } + + private ArraySet<Integer> createPinKeys() { + ArraySet<Integer> pinKeys = new ArraySet<>(); + // Pin the camera application. Default to the system property only if the experiment + // phenotype property is not set. + boolean shouldPinCamera = mConfiguredToPinCamera + && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT, + "pin_camera", + SystemProperties.getBoolean("pinner.pin_camera", false)); + if (shouldPinCamera) { + pinKeys.add(KEY_CAMERA); + } else if (DEBUG) { + Slog.i(TAG, "Pinner - skip pinning camera app"); + } + + if (mConfiguredToPinHome) { + pinKeys.add(KEY_HOME); + } + if (mConfiguredToPinAssistant) { + pinKeys.add(KEY_ASSISTANT); + } + + return pinKeys; + } + + private static boolean shouldPinSplitApks() { + // For now this is disabled by default bcause the pinlist support for split APKs are + // missing in the toolchain. This flag should be removed once it is ready. b/174697187. + return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT, + "pin_split_apks", false); + } + + private synchronized ArraySet<Integer> getPinKeys() { + return mPinKeys; + } + private void pinApps(int userHandle) { - for (int i = mPinKeys.size() - 1; i >= 0; i--) { - int key = mPinKeys.valueAt(i); + pinAppsInternal(userHandle, false); + } + + private void pinAppsWithUpdatedKeys(int userHandle) { + pinAppsInternal(userHandle, true); + } + + /** + * @param updateKeys True if the pinned app list has to be updated. This is true only when + * "pinner repin" shell command is requested. + */ + private void pinAppsInternal(int userHandle, boolean updateKeys) { + if (updateKeys) { + ArraySet<Integer> newKeys = createPinKeys(); + synchronized (this) { + // This code path demands preceding unpinApps() call. + if (!mPinnedApps.isEmpty()) { + Slog.e(TAG, "Attempted to update a list of apps, " + + "but apps were already pinned. Skipping."); + return; + } + + mPinKeys = newKeys; + } + } + + ArraySet<Integer> currentPinKeys = getPinKeys(); + for (int i = currentPinKeys.size() - 1; i >= 0; i--) { + int key = currentPinKeys.valueAt(i); pinApp(key, userHandle, true /* force */); } } @@ -610,19 +679,40 @@ public final class PinnerService extends SystemService { mPinnedApps.put(key, pinnedApp); } + // pin APK - int pinSizeLimit = getSizeLimitForKey(key); - String apk = appInfo.sourceDir; - PinnedFile pf = pinFile(apk, pinSizeLimit, /*attemptPinIntrospection=*/true); - if (pf == null) { - Slog.e(TAG, "Failed to pin " + apk); - return; - } - if (DEBUG) { - Slog.i(TAG, "Pinned " + pf.fileName); + final int pinSizeLimit = getSizeLimitForKey(key); + List<String> apks = new ArrayList<>(); + apks.add(appInfo.sourceDir); + + if (shouldPinSplitApks() && appInfo.splitSourceDirs != null) { + for (String splitApk : appInfo.splitSourceDirs) { + apks.add(splitApk); + } } - synchronized (this) { - pinnedApp.mFiles.add(pf); + + int apkPinSizeLimit = pinSizeLimit; + for (String apk: apks) { + if (apkPinSizeLimit <= 0) { + Slog.w(TAG, "Reached to the pin size limit. Skipping: " + apk); + // Continue instead of break to print all skipped APK names. + continue; + } + + PinnedFile pf = pinFile(apk, apkPinSizeLimit, /*attemptPinIntrospection=*/true); + if (pf == null) { + Slog.e(TAG, "Failed to pin " + apk); + continue; + } + + if (DEBUG) { + Slog.i(TAG, "Pinned " + pf.fileName); + } + synchronized (this) { + pinnedApp.mFiles.add(pf); + } + + apkPinSizeLimit -= pf.bytesPinned; } // determine the ABI from either ApplicationInfo or Build @@ -641,7 +731,7 @@ public final class PinnerService extends SystemService { //not pinning the oat/odex is not a fatal error for (String file : files) { - pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false); + PinnedFile pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false); if (pf != null) { synchronized (this) { if (PROP_PIN_ODEX) { @@ -981,6 +1071,42 @@ public final class PinnerService extends SystemService { } } } + + private void repin() { + sendUnpinAppsMessage(); + // TODO(morrita): Consider supporting non-system user. + sendPinAppsWithUpdatedKeysMessage(UserHandle.USER_SYSTEM); + } + + private void printError(FileDescriptor out, String message) { + PrintWriter writer = new PrintWriter(new FileOutputStream(out)); + writer.println(message); + writer.flush(); + } + + @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, + String[] args, ShellCallback callback, ResultReceiver resultReceiver) { + if (args.length < 1) { + printError(out, "Command is not given."); + resultReceiver.send(-1, null); + return; + } + + String command = args[0]; + switch (command) { + case "repin": + repin(); + break; + default: + printError(out, String.format( + "Unknown pinner command: %s. Supported commands: repin", command)); + resultReceiver.send(-1, null); + return; + } + + resultReceiver.send(0, null); + } } private static final class PinnedFile implements AutoCloseable { diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java index 9ba71dc5f4f7..e99bb245a2e0 100644 --- a/services/core/java/com/android/server/SensorPrivacyService.java +++ b/services/core/java/com/android/server/SensorPrivacyService.java @@ -283,11 +283,16 @@ public final class SensorPrivacyService extends SystemService { mIndividualEnabled.put(userId, userIndividualEnabled); if (!enable) { - // Remove any notifications prompting the user to disable sensory privacy - NotificationManager notificationManager = - mContext.getSystemService(NotificationManager.class); - - notificationManager.cancel(sensor); + long token = Binder.clearCallingIdentity(); + try { + // Remove any notifications prompting the user to disable sensory privacy + NotificationManager notificationManager = + mContext.getSystemService(NotificationManager.class); + + notificationManager.cancel(sensor); + } finally { + Binder.restoreCallingIdentity(token); + } } persistSensorPrivacyState(); } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index c951fd438b78..cd6a9fb447c9 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -1442,6 +1442,9 @@ class StorageManagerService extends IStorageManager.Stub mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); } else if (vol.type == VolumeInfo.TYPE_STUB) { + if (vol.disk.isStubVisible()) { + vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; + } vol.mountUserId = mCurrentUserId; mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); } else { @@ -1523,7 +1526,6 @@ class StorageManagerService extends IStorageManager.Stub } } - private void onVolumeStateChangedAsync(VolumeInfo vol, int oldState, int newState) { synchronized (mLock) { // Remember that we saw this volume so we're ready to accept user @@ -3272,6 +3274,27 @@ class StorageManagerService extends IStorageManager.Stub } } + /* + * Disable storage's app data isolation for testing. + */ + @Override + public void disableAppDataIsolation(String pkgName, int pid, int userId) { + final int callingUid = Binder.getCallingUid(); + if (callingUid != Process.ROOT_UID && callingUid != Process.SHELL_UID) { + throw new SecurityException("no permission to enable app visibility"); + } + final String[] sharedPackages = + mPmInternal.getSharedUserPackagesForPackage(pkgName, userId); + final int uid = mPmInternal.getPackageUid(pkgName, 0, userId); + final String[] packages = + sharedPackages.length != 0 ? sharedPackages : new String[]{pkgName}; + try { + mVold.unmountAppStorageDirs(uid, pid, packages); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + /** Not thread safe */ class AppFuseMountScope extends AppFuseBridge.MountScope { private boolean mMounted = false; diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index b76f32788f6c..dde182b90517 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -564,8 +564,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mPreciseDataConnectionStates.add(new ArrayMap<>()); mBarringInfo.add(i, new BarringInfo()); mTelephonyDisplayInfos[i] = null; - mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig( - PhysicalChannelConfig.CONNECTION_UNKNOWN,0)); + mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build()); } } @@ -656,8 +655,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mPreciseDataConnectionStates.add(new ArrayMap<>()); mBarringInfo.add(i, new BarringInfo()); mTelephonyDisplayInfos[i] = null; - mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig( - PhysicalChannelConfig.CONNECTION_UNKNOWN,0)); + mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build()); } mAppOps = mContext.getSystemService(AppOpsManager.class); diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index c191a78aad0e..2fdc7965675d 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -26,6 +26,7 @@ import android.app.AppOpsManager; import android.content.Context; import android.net.ConnectivityManager; import android.net.vcn.IVcnManagementService; +import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; import android.os.Binder; import android.os.Handler; @@ -495,4 +496,20 @@ public class VcnManagementService extends IVcnManagementService.Stub { return Collections.unmodifiableMap(mVcns); } } + + /** Adds the provided listener for receiving VcnUnderlyingNetworkPolicy updates. */ + @Override + public void addVcnUnderlyingNetworkPolicyListener( + IVcnUnderlyingNetworkPolicyListener listener) { + // TODO(b/175739863): implement policy listener registration + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** Removes the provided listener from receiving VcnUnderlyingNetworkPolicy updates. */ + @Override + public void removeVcnUnderlyingNetworkPolicyListener( + IVcnUnderlyingNetworkPolicyListener listener) { + // TODO(b/175739863): implement policy listener unregistration + throw new UnsupportedOperationException("Not yet implemented"); + } } diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 6a9715e49b99..2c83da55f275 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -28,6 +28,7 @@ import android.content.pm.PackageManagerInternal; import android.hardware.vibrator.IVibrator; import android.os.BatteryStats; import android.os.Binder; +import android.os.CombinedVibrationEffect; import android.os.ExternalVibration; import android.os.Handler; import android.os.IBinder; @@ -37,18 +38,15 @@ import android.os.IVibratorStateListener; import android.os.Looper; import android.os.PowerManager; import android.os.Process; -import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; import android.os.ShellCallback; import android.os.ShellCommand; -import android.os.SystemClock; import android.os.Trace; import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.Vibrator; import android.os.VibratorInfo; -import android.os.WorkSource; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -56,11 +54,11 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.util.DumpUtils; -import com.android.internal.util.FrameworkStatsLog; import com.android.server.vibrator.InputDeviceDelegate; import com.android.server.vibrator.Vibration; import com.android.server.vibrator.VibrationScaler; import com.android.server.vibrator.VibrationSettings; +import com.android.server.vibrator.VibrationThread; import com.android.server.vibrator.VibratorController; import com.android.server.vibrator.VibratorController.OnVibrationCompleteListener; @@ -90,10 +88,10 @@ public class VibratorService extends IVibratorService.Stub { private final LinkedList<Vibration.DebugInfo> mPreviousExternalVibrations; private final LinkedList<Vibration.DebugInfo> mPreviousVibrations; private final int mPreviousVibrationsLimit; - private final WorkSource mTmpWorkSource = new WorkSource(); private final Handler mH; private final Object mLock = new Object(); private final VibratorController mVibratorController; + private final VibrationCallbacks mVibrationCallbacks = new VibrationCallbacks(); private final Context mContext; private final PowerManager.WakeLock mWakeLock; @@ -104,61 +102,55 @@ public class VibratorService extends IVibratorService.Stub { private VibrationScaler mVibrationScaler; private InputDeviceDelegate mInputDeviceDelegate; - private volatile VibrateWaveformThread mThread; + private volatile VibrationThread mThread; @GuardedBy("mLock") private Vibration mCurrentVibration; - @GuardedBy("mLock") - private VibrationDeathRecipient mCurrentVibrationDeathRecipient; private int mCurVibUid = -1; private ExternalVibrationHolder mCurrentExternalVibration; /** - * Implementation of {@link OnVibrationCompleteListener} with a weak reference to this service. + * Implementation of {@link VibrationThread.VibrationCallbacks} that reports finished + * vibrations. */ - private static final class VibrationCompleteListener implements OnVibrationCompleteListener { - private WeakReference<VibratorService> mServiceRef; - - VibrationCompleteListener(VibratorService service) { - mServiceRef = new WeakReference<>(service); - } + private final class VibrationCallbacks implements VibrationThread.VibrationCallbacks { @Override - public void onComplete(int vibratorId, long vibrationId) { - VibratorService service = mServiceRef.get(); - if (service != null) { - service.onVibrationComplete(vibrationId); - } + public void prepareSyncedVibration(int requiredCapabilities, int[] vibratorIds) { } - } - /** Death recipient to bind {@link Vibration}. */ - private final class VibrationDeathRecipient implements IBinder.DeathRecipient { - - private final Vibration mVibration; - - private VibrationDeathRecipient(Vibration vibration) { - mVibration = vibration; + @Override + public void triggerSyncedVibration(long vibrationId) { } @Override - public void binderDied() { + public void onVibrationEnded(long vibrationId, Vibration.Status status) { + if (DEBUG) { + Slog.d(TAG, "Vibration thread finished with status " + status); + } synchronized (mLock) { - if (mVibration == mCurrentVibration) { - if (DEBUG) { - Slog.d(TAG, "Vibration finished because binder died, cleaning up"); - } - doCancelVibrateLocked(Vibration.Status.CANCELLED); - } + mThread = null; + reportFinishVibrationLocked(status); } } + } - private void linkToDeath() throws RemoteException { - mVibration.token.linkToDeath(this, 0); + /** + * Implementation of {@link OnVibrationCompleteListener} with a weak reference to this service. + */ + private static final class VibrationCompleteListener implements OnVibrationCompleteListener { + private WeakReference<VibratorService> mServiceRef; + + VibrationCompleteListener(VibratorService service) { + mServiceRef = new WeakReference<>(service); } - private void unlinkToDeath() { - mVibration.token.unlinkToDeath(this, 0); + @Override + public void onComplete(int vibratorId, long vibrationId) { + VibratorService service = mServiceRef.get(); + if (service != null) { + service.onVibrationComplete(vibratorId, vibrationId); + } } } @@ -262,13 +254,20 @@ public class VibratorService extends IVibratorService.Stub { /** Callback for when vibration is complete, to be called by native. */ @VisibleForTesting - public void onVibrationComplete(long vibrationId) { + public void onVibrationComplete(int vibratorId, long vibrationId) { synchronized (mLock) { if (mCurrentVibration != null && mCurrentVibration.id == vibrationId) { if (DEBUG) { - Slog.d(TAG, "Vibration finished by callback, cleaning up"); + Slog.d(TAG, "Vibration onComplete callback, notifying VibrationThread"); + } + if (mThread != null) { + // Let the thread playing the vibration handle the callback, since it might be + // expecting the vibrator to turn off multiple times during a single vibration. + mThread.vibratorComplete(vibratorId); + } else { + // No vibration is playing in the thread, but clean up service just in case. + doCancelVibrateLocked(Vibration.Status.FINISHED); } - doCancelVibrateLocked(Vibration.Status.FINISHED); } } } @@ -354,6 +353,17 @@ public class VibratorService extends IVibratorService.Stub { return true; } + private VibrationEffect fixupVibrationEffect(VibrationEffect effect) { + if (effect instanceof VibrationEffect.Prebaked + && ((VibrationEffect.Prebaked) effect).shouldFallback()) { + VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect; + VibrationEffect fallback = mVibrationSettings.getFallbackEffect(prebaked.getId()); + return new VibrationEffect.Prebaked(prebaked.getId(), prebaked.getEffectStrength(), + fallback); + } + return effect; + } + private VibrationAttributes fixupVibrationAttributes(VibrationAttributes attrs) { if (attrs == null) { attrs = DEFAULT_ATTRIBUTES; @@ -388,16 +398,16 @@ public class VibratorService extends IVibratorService.Stub { if (!verifyVibrationEffect(effect)) { return; } - + effect = fixupVibrationEffect(effect); attrs = fixupVibrationAttributes(attrs); - Vibration vib = new Vibration(token, mNextVibrationId.getAndIncrement(), effect, attrs, - uid, opPkg, reason); + Vibration vib = new Vibration(token, mNextVibrationId.getAndIncrement(), + CombinedVibrationEffect.createSynced(effect), attrs, uid, opPkg, reason); // If our current vibration is longer than the new vibration and is the same amplitude, // then just let the current one finish. synchronized (mLock) { VibrationEffect currentEffect = - mCurrentVibration == null ? null : mCurrentVibration.getEffect(); + mCurrentVibration == null ? null : getEffect(mCurrentVibration); if (effect instanceof VibrationEffect.OneShot && currentEffect instanceof VibrationEffect.OneShot) { VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect; @@ -446,7 +456,6 @@ public class VibratorService extends IVibratorService.Stub { endVibrationLocked(vib, Vibration.Status.IGNORED_BACKGROUND); return; } - linkVibrationLocked(vib); final long ident = Binder.clearCallingIdentity(); try { doCancelVibrateLocked(Vibration.Status.CANCELLED); @@ -474,6 +483,10 @@ public class VibratorService extends IVibratorService.Stub { return effect.getDuration() == Long.MAX_VALUE; } + private static <T extends VibrationEffect> T getEffect(Vibration vib) { + return (T) ((CombinedVibrationEffect.Mono) vib.getEffect()).getEffect(); + } + private void endVibrationLocked(Vibration vib, Vibration.Status status) { final LinkedList<Vibration.DebugInfo> previousVibrations; switch (vib.attrs.getUsage()) { @@ -527,7 +540,6 @@ public class VibratorService extends IVibratorService.Stub { @GuardedBy("mLock") private void doCancelVibrateLocked(Vibration.Status status) { - Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelVibrateLocked"); try { if (mThread != null) { @@ -547,18 +559,6 @@ public class VibratorService extends IVibratorService.Stub { } } - // Callback for whenever the current vibration has finished played out - public void onVibrationFinished() { - if (DEBUG) { - Slog.d(TAG, "Vibration finished, cleaning up"); - } - synchronized (mLock) { - // Make sure the vibration is really done. This also reports that the vibration is - // finished. - doCancelVibrateLocked(Vibration.Status.FINISHED); - } - } - @GuardedBy("mLock") private void startVibrationLocked(final Vibration vib) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked"); @@ -579,24 +579,20 @@ public class VibratorService extends IVibratorService.Stub { try { // Set current vibration before starting it, so callback will work. mCurrentVibration = vib; - VibrationEffect effect = vib.getEffect(); - if (effect instanceof VibrationEffect.OneShot) { - Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); - doVibratorOn(vib); - } else if (effect instanceof VibrationEffect.Waveform) { - Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); - doVibratorWaveformEffectLocked(vib); - } else if (effect instanceof VibrationEffect.Prebaked) { - Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); - doVibratorPrebakedEffectLocked(vib); - } else if (effect instanceof VibrationEffect.Composed) { - Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); - doVibratorComposedEffectLocked(vib); - } else { - Slog.e(TAG, "Unknown vibration type, ignoring"); - endVibrationLocked(vib, Vibration.Status.IGNORED_UNKNOWN_VIBRATION); - // The set current vibration is not actually playing, so drop it. + VibrationEffect effect = getEffect(vib); + Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); + boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable( + vib.uid, vib.opPkg, vib.getEffect(), vib.reason, vib.attrs); + if (inputDevicesAvailable) { + // The set current vibration is no longer being played by this service, so drop it. mCurrentVibration = null; + endVibrationLocked(vib, Vibration.Status.FORWARDED_TO_INPUT_DEVICES); + } else { + // mThread better be null here. doCancelVibrate should always be + // called before startVibrationInnerLocked + mThread = new VibrationThread(vib, mVibratorController, mWakeLock, + mBatteryStatsService, mVibrationCallbacks); + mThread.start(); } } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); @@ -667,13 +663,13 @@ public class VibratorService extends IVibratorService.Stub { @GuardedBy("mLock") private void reportFinishVibrationLocked(Vibration.Status status) { + Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked"); try { if (mCurrentVibration != null) { endVibrationLocked(mCurrentVibration, status); mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid, mCurrentVibration.opPkg); - unlinkVibrationLocked(); mCurrentVibration = null; } } finally { @@ -681,30 +677,6 @@ public class VibratorService extends IVibratorService.Stub { } } - @GuardedBy("mLock") - private void linkVibrationLocked(Vibration vib) { - // Unlink previously linked vibration, if any. - unlinkVibrationLocked(); - // Only link against waveforms since they potentially don't have a finish if - // they're repeating. Let other effects just play out until they're done. - if (vib.getEffect() instanceof VibrationEffect.Waveform) { - try { - mCurrentVibrationDeathRecipient = new VibrationDeathRecipient(vib); - mCurrentVibrationDeathRecipient.linkToDeath(); - } catch (RemoteException e) { - return; - } - } - } - - @GuardedBy("mLock") - private void unlinkVibrationLocked() { - if (mCurrentVibrationDeathRecipient != null) { - mCurrentVibrationDeathRecipient.unlinkToDeath(); - mCurrentVibrationDeathRecipient = null; - } - } - private void updateVibrators() { synchronized (mLock) { mInputDeviceDelegate.updateInputDeviceVibrators( @@ -715,40 +687,12 @@ public class VibratorService extends IVibratorService.Stub { } } - private void doVibratorOn(Vibration vib) { - Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn"); - try { - final VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.getEffect(); - if (DEBUG) { - Slog.d(TAG, "Turning vibrator on for " + oneShot.getDuration() + " ms" - + " with amplitude " + oneShot.getAmplitude() + "."); - } - boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable( - vib.uid, vib.opPkg, oneShot, vib.reason, vib.attrs); - if (inputDevicesAvailable) { - // The set current vibration is no longer being played by this service, so drop it. - mCurrentVibration = null; - endVibrationLocked(vib, Vibration.Status.FORWARDED_TO_INPUT_DEVICES); - } else { - noteVibratorOnLocked(vib.uid, oneShot.getDuration()); - // Note: ordering is important here! Many haptic drivers will reset their - // amplitude when enabled, so we always have to enable first, then set the - // amplitude. - mVibratorController.on(oneShot.getDuration(), vib.id); - mVibratorController.setAmplitude(oneShot.getAmplitude()); - } - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); - } - } - private void doVibratorOff() { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOff"); try { if (DEBUG) { Slog.d(TAG, "Turning vibrator off."); } - noteVibratorOffLocked(); boolean inputDevicesAvailable = mInputDeviceDelegate.cancelVibrateIfAvailable(); if (!inputDevicesAvailable) { mVibratorController.off(); @@ -758,95 +702,6 @@ public class VibratorService extends IVibratorService.Stub { } } - @GuardedBy("mLock") - private void doVibratorWaveformEffectLocked(Vibration vib) { - Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorWaveformEffectLocked"); - try { - boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable( - vib.uid, vib.opPkg, vib.getEffect(), vib.reason, vib.attrs); - if (inputDevicesAvailable) { - // The set current vibration is no longer being played by this service, so drop it. - mCurrentVibration = null; - endVibrationLocked(vib, Vibration.Status.FORWARDED_TO_INPUT_DEVICES); - } else { - // mThread better be null here. doCancelVibrate should always be - // called before startNextVibrationLocked or startVibrationLocked. - mThread = new VibrateWaveformThread(vib); - mThread.start(); - } - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); - } - } - - @GuardedBy("mLock") - private void doVibratorPrebakedEffectLocked(Vibration vib) { - Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorPrebakedEffectLocked"); - try { - final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.getEffect(); - // Input devices don't support prebaked effect, so skip trying it with them and allow - // fallback to be attempted. - if (!mInputDeviceDelegate.isAvailable()) { - long duration = mVibratorController.on(prebaked, vib.id); - if (duration > 0) { - noteVibratorOnLocked(vib.uid, duration); - return; - } - } - endVibrationLocked(vib, Vibration.Status.IGNORED_UNSUPPORTED); - // The set current vibration is not actually playing, so drop it. - mCurrentVibration = null; - - if (!prebaked.shouldFallback()) { - return; - } - VibrationEffect effect = mVibrationSettings.getFallbackEffect(prebaked.getId()); - if (effect == null) { - Slog.w(TAG, "Failed to play prebaked effect, no fallback"); - return; - } - Vibration fallbackVib = new Vibration(vib.token, mNextVibrationId.getAndIncrement(), - effect, vib.attrs, vib.uid, vib.opPkg, vib.reason + " (fallback)"); - // Set current vibration before starting it, so callback will work. - mCurrentVibration = fallbackVib; - linkVibrationLocked(fallbackVib); - applyVibrationIntensityScalingLocked(fallbackVib); - startVibrationInnerLocked(fallbackVib); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); - } - } - - @GuardedBy("mLock") - private void doVibratorComposedEffectLocked(Vibration vib) { - Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorComposedEffectLocked"); - - try { - final VibrationEffect.Composed composed = (VibrationEffect.Composed) vib.getEffect(); - boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable( - vib.uid, vib.opPkg, composed, vib.reason, vib.attrs); - if (inputDevicesAvailable) { - // The set current vibration is no longer being played by this service, so drop it. - mCurrentVibration = null; - endVibrationLocked(vib, Vibration.Status.FORWARDED_TO_INPUT_DEVICES); - return; - } else if (!mVibratorController.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) { - // The set current vibration is not actually playing, so drop it. - mCurrentVibration = null; - endVibrationLocked(vib, Vibration.Status.IGNORED_UNSUPPORTED); - return; - } - - mVibratorController.on(composed, vib.id); - - // Composed effects don't actually give us an estimated duration, so we just guess here. - noteVibratorOnLocked(vib.uid, 10 * composed.getPrimitiveEffects().size()); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); - } - - } - private boolean isSystemHapticFeedback(Vibration vib) { if (vib.attrs.getUsage() != VibrationAttributes.USAGE_TOUCH) { return false; @@ -854,27 +709,6 @@ public class VibratorService extends IVibratorService.Stub { return vib.uid == Process.SYSTEM_UID || vib.uid == 0 || mSystemUiPackage.equals(vib.opPkg); } - private void noteVibratorOnLocked(int uid, long millis) { - try { - mBatteryStatsService.noteVibratorOn(uid, millis); - FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED, uid, null, - FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON, millis); - mCurVibUid = uid; - } catch (RemoteException e) { - } - } - - private void noteVibratorOffLocked() { - if (mCurVibUid >= 0) { - try { - mBatteryStatsService.noteVibratorOff(mCurVibUid); - FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED, - mCurVibUid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF, 0); - } catch (RemoteException e) { } - mCurVibUid = -1; - } - } - private void dumpInternal(PrintWriter pw) { pw.println("Vibrator Service:"); synchronized (mLock) { @@ -972,156 +806,6 @@ public class VibratorService extends IVibratorService.Stub { proto.flush(); } - /** Thread that plays a single {@link VibrationEffect.Waveform}. */ - private class VibrateWaveformThread extends Thread { - private final VibrationEffect.Waveform mWaveform; - private final Vibration mVibration; - - private boolean mForceStop; - - VibrateWaveformThread(Vibration vib) { - mWaveform = (VibrationEffect.Waveform) vib.getEffect(); - mVibration = new Vibration(vib.token, /* id= */ 0, /* effect= */ null, vib.attrs, - vib.uid, vib.opPkg, vib.reason); - mTmpWorkSource.set(vib.uid); - mWakeLock.setWorkSource(mTmpWorkSource); - } - - private void delayLocked(long wakeUpTime) { - Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "delayLocked"); - try { - long durationRemaining = wakeUpTime - SystemClock.uptimeMillis(); - while (durationRemaining > 0) { - try { - this.wait(durationRemaining); - } catch (InterruptedException e) { - } - if (mForceStop) { - break; - } - durationRemaining = wakeUpTime - SystemClock.uptimeMillis(); - } - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); - } - } - - public void run() { - Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY); - mWakeLock.acquire(); - try { - boolean finished = playWaveform(); - if (finished) { - onVibrationFinished(); - } - } finally { - mWakeLock.release(); - } - } - - /** - * Play the waveform. - * - * @return true if it finished naturally, false otherwise (e.g. it was canceled). - */ - public boolean playWaveform() { - Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playWaveform"); - try { - synchronized (this) { - final long[] timings = mWaveform.getTimings(); - final int[] amplitudes = mWaveform.getAmplitudes(); - final int len = timings.length; - final int repeat = mWaveform.getRepeatIndex(); - - int index = 0; - long nextStepStartTime = SystemClock.uptimeMillis(); - long nextVibratorStopTime = 0; - while (!mForceStop) { - if (index < len) { - final int amplitude = amplitudes[index]; - final long duration = timings[index++]; - if (duration <= 0) { - continue; - } - if (amplitude != 0) { - long now = SystemClock.uptimeMillis(); - if (nextVibratorStopTime <= now) { - // Telling the vibrator to start multiple times usually causes - // effects to feel "choppy" because the motor resets at every on - // command. Instead we figure out how long our next "on" period - // is going to be, tell the motor to stay on for the full - // duration, and then wake up to change the amplitude at the - // appropriate intervals. - long onDuration = getTotalOnDuration( - timings, amplitudes, index - 1, repeat); - mVibration.updateEffect( - VibrationEffect.createOneShot(onDuration, amplitude)); - doVibratorOn(mVibration); - nextVibratorStopTime = now + onDuration; - } else { - // Vibrator is already ON, so just change its amplitude. - mVibratorController.setAmplitude(amplitude); - } - } else { - // Previous vibration should have already finished, but we make sure - // the vibrator will be off for the next step when amplitude is 0. - doVibratorOff(); - } - - // We wait until the time this waveform step was supposed to end, - // calculated from the time it was supposed to start. All start times - // are calculated from the waveform original start time by adding the - // input durations. Any scheduling or processing delay should not affect - // this step's perceived total duration. They will be amortized here. - nextStepStartTime += duration; - delayLocked(nextStepStartTime); - } else if (repeat < 0) { - break; - } else { - index = repeat; - } - } - return !mForceStop; - } - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); - } - } - - public void cancel() { - synchronized (this) { - mThread.mForceStop = true; - mThread.notify(); - } - } - - /** - * Get the duration the vibrator will be on starting at startIndex until the next time it's - * off. - */ - private long getTotalOnDuration( - long[] timings, int[] amplitudes, int startIndex, int repeatIndex) { - int i = startIndex; - long timing = 0; - while (amplitudes[i] != 0) { - timing += timings[i++]; - if (i >= timings.length) { - if (repeatIndex >= 0) { - i = repeatIndex; - // prevent infinite loop - repeatIndex = -1; - } else { - break; - } - } - if (i == startIndex) { - return 1000; - } - } - return timing; - } - } - /** Point of injection for test dependencies */ @VisibleForTesting static class Injector { diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 88bb1a012a12..5cc32743af34 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1692,7 +1692,7 @@ public final class ActiveServices { } try { - String ignoreForeground = null; + boolean ignoreForeground = false; final int mode = mAm.getAppOpsManager().checkOpNoThrow( AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName); switch (mode) { @@ -1702,9 +1702,9 @@ public final class ActiveServices { break; case AppOpsManager.MODE_IGNORED: // Whoops, silently ignore this. - ignoreForeground = "Service.startForeground() not allowed due to app op: " - + "service " + r.shortInstanceName; - Slog.w(TAG, ignoreForeground); + Slog.w(TAG, "Service.startForeground() not allowed due to app op: service " + + r.shortInstanceName); + ignoreForeground = true; break; default: throw new SecurityException("Foreground not allowed as per app op"); @@ -1712,18 +1712,19 @@ public final class ActiveServices { // Apps that are TOP or effectively similar may call startForeground() on // their services even if they are restricted from doing that while in bg. - if (ignoreForeground == null + if (!ignoreForeground && !appIsTopLocked(r.appInfo.uid) && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) { - ignoreForeground = "Service.startForeground() not allowed due to bg restriction" - + ":service " + r.shortInstanceName; - Slog.w(TAG, ignoreForeground); + Slog.w(TAG, + "Service.startForeground() not allowed due to bg restriction: service " + + r.shortInstanceName); // Back off of any foreground expectations around this service, since we've // just turned down its fg request. updateServiceForegroundLocked(r.app, false); + ignoreForeground = true; } - if (ignoreForeground == null) { + if (!ignoreForeground) { if (isFgsBgStart(r.mAllowStartForeground)) { if (!r.mLoggedInfoAllowStartForeground) { Slog.wtf(TAG, "Background started FGS " @@ -1732,12 +1733,17 @@ public final class ActiveServices { } if (r.mAllowStartForeground == FGS_FEATURE_DENIED && isBgFgsRestrictionEnabled(r)) { - ignoreForeground = "Service.startForeground() not allowed due to " + final String msg = "Service.startForeground() not allowed due to " + "mAllowStartForeground false: service " + r.shortInstanceName; - Slog.w(TAG, ignoreForeground); + Slog.w(TAG, msg); showFgsBgRestrictedNotificationLocked(r); updateServiceForegroundLocked(r.app, true); + ignoreForeground = true; + if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, + r.appInfo.uid)) { + throw new IllegalStateException(msg); + } } } } @@ -1746,7 +1752,7 @@ public final class ActiveServices { // services, so now that we've enforced the startForegroundService() contract // we only do the machinery of making the service foreground when the app // is not restricted. - if (ignoreForeground == null) { + if (!ignoreForeground) { if (r.foregroundId != id) { cancelForegroundNotificationLocked(r); r.foregroundId = id; @@ -1808,10 +1814,6 @@ public final class ActiveServices { if (DEBUG_FOREGROUND_SERVICE) { Slog.d(TAG, "Suppressing startForeground() for FAS " + r); } - if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, r.appInfo.uid) - && isBgFgsRestrictionEnabled(r)) { - throw new IllegalStateException(ignoreForeground); - } } } finally { if (stopProcStatsOp) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b587f1b2599d..1f48aeb91de8 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -32,6 +32,7 @@ import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.AppOpsManager.OP_NONE; +import static android.app.BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED; import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT; import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES; import static android.content.pm.PackageManager.MATCH_ALL; @@ -1085,11 +1086,13 @@ public class ActivityManagerService extends IActivityManager.Stub final int targetUid; final long duration; final String tag; + final int type; - PendingTempWhitelist(int _targetUid, long _duration, String _tag) { + PendingTempWhitelist(int _targetUid, long _duration, String _tag, int _type) { targetUid = _targetUid; duration = _duration; tag = _tag; + type = _type; } void dumpDebug(ProtoOutputStream proto, long fieldId) { @@ -1097,6 +1100,7 @@ public class ActivityManagerService extends IActivityManager.Stub proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TARGET_UID, targetUid); proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.DURATION_MS, duration); proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TAG, tag); + proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TYPE, type); proto.end(token); } } @@ -5526,8 +5530,7 @@ public class ActivityManagerService extends IActivityManager.Stub } boolean isWhitelistedForFgsStartLocked(int uid) { - final int appId = UserHandle.getAppId(uid); - return Arrays.binarySearch(mDeviceIdleExceptIdleWhitelist, appId) >= 0 + return Arrays.binarySearch(mDeviceIdleExceptIdleWhitelist, UserHandle.getAppId(uid)) >= 0 || mFgsStartTempAllowList.isAllowed(uid); } @@ -8302,6 +8305,7 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized(this) { mConstants.dump(pw); mOomAdjuster.dumpCachedAppOptimizerSettings(pw); + mOomAdjuster.dumpCacheOomRankerSettings(pw); pw.println(); if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); @@ -8722,6 +8726,7 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { mConstants.dump(pw); mOomAdjuster.dumpCachedAppOptimizerSettings(pw); + mOomAdjuster.dumpCacheOomRankerSettings(pw); } } else if ("services".equals(cmd) || "s".equals(cmd)) { if (dumpClient) { @@ -9296,6 +9301,8 @@ public class ActivityManagerService extends IActivityManager.Stub TimeUtils.formatDuration(ptw.duration, pw); pw.print(" "); pw.println(ptw.tag); + pw.print(" "); + pw.print(ptw.type); } } } @@ -15357,11 +15364,12 @@ public class ActivityManagerService extends IActivityManager.Stub */ @GuardedBy("this") void tempWhitelistUidLocked(int targetUid, long duration, String tag, int type) { - mPendingTempWhitelist.put(targetUid, new PendingTempWhitelist(targetUid, duration, tag)); + mPendingTempWhitelist.put(targetUid, + new PendingTempWhitelist(targetUid, duration, tag, type)); setUidTempWhitelistStateLocked(targetUid, true); mUiHandler.obtainMessage(PUSH_TEMP_WHITELIST_UI_MSG).sendToTarget(); - if (type == BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { + if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { mFgsStartTempAllowList.add(targetUid, duration); } } @@ -15387,7 +15395,7 @@ public class ActivityManagerService extends IActivityManager.Stub for (int i = 0; i < N; i++) { PendingTempWhitelist ptw = list[i]; mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid, - ptw.duration, true, ptw.tag); + ptw.duration, ptw.type, true, ptw.tag); } } @@ -15404,8 +15412,8 @@ public class ActivityManagerService extends IActivityManager.Stub } @GuardedBy("this") - final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) { - mOomAdjuster.setAppIdTempWhitelistStateLocked(appId, onWhitelist); + final void setAppIdTempWhitelistStateLocked(int uid, boolean onWhitelist) { + mOomAdjuster.setAppIdTempWhitelistStateLocked(uid, onWhitelist); } @GuardedBy("this") @@ -15713,6 +15721,16 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public boolean startProfile(@UserIdInt int userId) { + return mUserController.startProfile(userId); + } + + @Override + public boolean stopProfile(@UserIdInt int userId) { + return mUserController.stopProfile(userId); + } + + @Override public UserInfo getCurrentUser() { return mUserController.getCurrentUser(); } @@ -15997,10 +16015,16 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void updateDeviceIdleTempWhitelist(int[] appids, int changingAppId, boolean adding) { + public void updateDeviceIdleTempWhitelist(int[] appids, int changingUid, boolean adding, + long durationMs, @BroadcastOptions.TempAllowListType int type) { synchronized (ActivityManagerService.this) { mDeviceIdleTempWhitelist = appids; - setAppIdTempWhitelistStateLocked(changingAppId, adding); + if (adding) { + if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { + mFgsStartTempAllowList.add(changingUid, durationMs); + } + } + setAppIdTempWhitelistStateLocked(changingUid, adding); } } diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 6fe934ee6937..ada7eeab9da3 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -17,21 +17,24 @@ package com.android.server.am; import static com.android.internal.power.MeasuredEnergyArray.SUBSYSTEM_DISPLAY; +import android.annotation.NonNull; import android.annotation.Nullable; import android.bluetooth.BluetoothActivityEnergyInfo; import android.bluetooth.BluetoothAdapter; import android.content.Context; +import android.hardware.power.stats.EnergyConsumerId; +import android.hardware.power.stats.EnergyConsumerResult; import android.net.wifi.WifiManager; import android.os.BatteryStats; import android.os.Bundle; import android.os.OutcomeReceiver; import android.os.Parcelable; import android.os.Process; -import android.os.ServiceManager; import android.os.SynchronousResultReceiver; import android.os.SystemClock; import android.os.ThreadLocalWorkSource; import android.os.connectivity.WifiActivityEnergyInfo; +import android.power.PowerStatsInternal; import android.telephony.ModemActivityInfo; import android.telephony.TelephonyManager; import android.util.IntArray; @@ -41,8 +44,10 @@ import android.util.SparseLongArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.power.MeasuredEnergyArray; +import com.android.internal.power.MeasuredEnergyStats; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.function.pooled.PooledLambda; +import com.android.server.LocalServices; import libcore.util.EmptyArray; @@ -91,6 +96,8 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { }); private final Context mContext; + + @GuardedBy("mStats") private final BatteryStatsImpl mStats; @GuardedBy("this") @@ -123,6 +130,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { @GuardedBy("this") private Future<?> mBatteryLevelSync; + // If both mStats and mWorkerLock need to be synchronized, mWorkerLock must be acquired first. private final Object mWorkerLock = new Object(); @GuardedBy("mWorkerLock") @@ -131,6 +139,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { @GuardedBy("mWorkerLock") private TelephonyManager mTelephony = null; + @GuardedBy("mWorkerLock") + private PowerStatsInternal mPowerStatsInternal = null; + // WiFi keeps an accumulated total of stats, unlike Bluetooth. // Keep the last WiFi stats so we can compute a delta. @GuardedBy("mWorkerLock") @@ -139,7 +150,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { /** Snapshot of measured energies, or null if no measured energies are supported. */ @GuardedBy("mWorkerLock") - private final @Nullable MeasuredEnergySnapshot mMeasuredEnergySnapshot; + private @Nullable MeasuredEnergySnapshot mMeasuredEnergySnapshot = null; /** * Timestamp at which all external stats were last collected in @@ -148,13 +159,27 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { @GuardedBy("this") private long mLastCollectionTimeStamp; - BatteryExternalStatsWorker(Context context, BatteryStatsImpl stats, - @Nullable MeasuredEnergyArray initialMeasuredEnergies) { + BatteryExternalStatsWorker(Context context, BatteryStatsImpl stats) { mContext = context; mStats = stats; + } - mMeasuredEnergySnapshot = initialMeasuredEnergies == null ? - null : new MeasuredEnergySnapshot(initialMeasuredEnergies); + public void systemServicesReady() { + final WifiManager wm = mContext.getSystemService(WifiManager.class); + final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); + final PowerStatsInternal psi = LocalServices.getService(PowerStatsInternal.class); + final MeasuredEnergyArray initialMeasuredEnergies = getEnergyConsumptionData(psi); + final boolean[] supportedBuckets = getSupportedEnergyBuckets(initialMeasuredEnergies); + synchronized (mWorkerLock) { + mWifiManager = wm; + mTelephony = tm; + mPowerStatsInternal = psi; + mMeasuredEnergySnapshot = initialMeasuredEnergies == null + ? null : new MeasuredEnergySnapshot(initialMeasuredEnergies); + synchronized (mStats) { + mStats.initMeasuredEnergyStatsLocked(supportedBuckets); + } + } } @Override @@ -433,14 +458,6 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) { // We were asked to fetch WiFi data. - if (mWifiManager == null && ServiceManager.getService(Context.WIFI_SERVICE) != null) { - // this code is reached very early in the boot process, before Wifi Service has - // been registered. Check that ServiceManager.getService() returns a non null - // value before calling mContext.getSystemService(), since otherwise - // getSystemService() will throw a ServiceNotFoundException. - mWifiManager = mContext.getSystemService(WifiManager.class); - } - // Only fetch WiFi power data if it is supported. if (mWifiManager != null && mWifiManager.isEnhancedPowerReportingSupported()) { SynchronousResultReceiver tempWifiReceiver = new SynchronousResultReceiver("wifi"); @@ -478,10 +495,6 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) { // We were asked to fetch Telephony data. - if (mTelephony == null) { - mTelephony = mContext.getSystemService(TelephonyManager.class); - } - if (mTelephony != null) { CompletableFuture<ModemActivityInfo> temp = new CompletableFuture<>(); mTelephony.requestModemActivityInfo(Runnable::run, @@ -689,17 +702,91 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { return delta; } - // TODO(b/172934873): Evaluate a safe way to query the HAL without holding mStats + /** + * Map the {@link MeasuredEnergyArray.MeasuredEnergySubsystem}s in the given energyArray to + * their corresponding {@link MeasuredEnergyStats.EnergyBucket}s. + * + * @return array with true for index i if energy bucket i is supported. + */ + private static @Nullable boolean[] getSupportedEnergyBuckets(MeasuredEnergyArray energyArray) { + if (energyArray == null) { + return null; + } + final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS]; + final int size = energyArray.size(); + for (int energyIdx = 0; energyIdx < size; energyIdx++) { + switch (energyArray.getSubsystem(energyIdx)) { + case MeasuredEnergyArray.SUBSYSTEM_DISPLAY: + buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON] = true; + buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE] = true; + buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_OTHER] = true; + break; + } + } + return buckets; + } + + /** + * Get a {@link MeasuredEnergyArray} with the latest + * {@link MeasuredEnergyArray.MeasuredEnergySubsystem} energy usage since boot. + * + * TODO(b/176988041): Replace {@link MeasuredEnergyArray} usage with {@link + * EnergyConsumerResult}[] + */ + private static @Nullable + MeasuredEnergyArray getEnergyConsumptionData(@NonNull PowerStatsInternal psi) { + final EnergyConsumerResult[] results; + try { + results = psi.getEnergyConsumedAsync(new int[0]) + .get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } catch (Exception e) { + Slog.e(TAG, "Failed to getEnergyConsumedAsync", e); + return null; + } + if (results == null) return null; + final int size = results.length; + final int[] subsystems = new int[size]; + final long[] energyUJ = new long[size]; + + for (int i = 0; i < size; i++) { + final EnergyConsumerResult consumer = results[i]; + final int subsystem; + switch (consumer.energyConsumerId) { + case EnergyConsumerId.DISPLAY: + subsystem = MeasuredEnergyArray.SUBSYSTEM_DISPLAY; + break; + default: + continue; + } + subsystems[i] = subsystem; + energyUJ[i] = consumer.energyUWs; + } + return new MeasuredEnergyArray() { + @Override + public int getSubsystem(int index) { + return subsystems[index]; + } + + @Override + public long getEnergy(int index) { + return energyUJ[index]; + } + + @Override + public int size() { + return size; + } + }; + } + /** Fetch MeasuredEnergyArray for supported subsystems based on the given updateFlags. */ @GuardedBy("mWorkerLock") private @Nullable MeasuredEnergyArray getMeasuredEnergyLocked(@ExternalUpdateFlag int flags) { - if (mMeasuredEnergySnapshot == null) return null; + if (mMeasuredEnergySnapshot == null || mPowerStatsInternal == null) return null; if (flags == UPDATE_ALL) { // Gotta catch 'em all... including custom (non-specific) subsystems - synchronized (mStats) { - return mStats.getEnergyConsumptionDataLocked(); - } + return getEnergyConsumptionData(mPowerStatsInternal); } final List<Integer> energyConsumerIds = new ArrayList<>(); @@ -710,10 +797,8 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { if (energyConsumerIds.isEmpty()) { return null; } - synchronized (mStats) { - // TODO: Query *specific* subsystems from HAL based on energyConsumerIds.toArray() - return mStats.getEnergyConsumptionDataLocked(); - } + // TODO: Query *specific* subsystems from HAL based on energyConsumerIds.toArray() + return getEnergyConsumptionData(mPowerStatsInternal); } @GuardedBy("mWorkerLock") diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 2f7c5234d982..405ee7266578 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -16,14 +16,11 @@ package com.android.server.am; -import android.annotation.Nullable; import android.bluetooth.BluetoothActivityEnergyInfo; import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.hardware.power.stats.EnergyConsumerId; -import android.hardware.power.stats.EnergyConsumerResult; import android.os.BatteryStats; import android.os.BatteryStatsInternal; import android.os.BatteryUsageStats; @@ -65,9 +62,6 @@ import com.android.internal.os.BinderCallsStats; import com.android.internal.os.PowerProfile; import com.android.internal.os.RailStats; import com.android.internal.os.RpmStats; -import com.android.internal.power.MeasuredEnergyArray; -import com.android.internal.power.MeasuredEnergyArray.MeasuredEnergySubsystem; -import com.android.internal.power.MeasuredEnergyStats; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.ParseUtils; @@ -75,7 +69,6 @@ import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; import com.android.server.Watchdog; import com.android.server.pm.UserManagerInternal; -import com.android.server.powerstats.PowerStatsHALWrapper; import java.io.File; import java.io.FileDescriptor; @@ -126,8 +119,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub private CharBuffer mUtf16BufferStat = CharBuffer.allocate(MAX_LOW_POWER_STATS_SIZE); private static final int MAX_LOW_POWER_STATS_SIZE = 4096; - private final PowerStatsHALWrapper.IPowerStatsHALWrapper mPowerStatsHALWrapper; - private final HandlerThread mHandlerThread; private final Handler mHandler; private final Object mLock = new Object(); @@ -199,43 +190,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } - @Override - public @Nullable MeasuredEnergyArray getEnergyConsumptionData() { - final EnergyConsumerResult[] results = mPowerStatsHALWrapper.getEnergyConsumed(new int[0]); - if (results == null) return null; - final int size = results.length; - final int[] subsystems = new int[size]; - final long[] energyUJ = new long[size]; - - for (int i = 0; i < size; i++) { - final EnergyConsumerResult consumer = results[i]; - final int subsystem; - switch (consumer.energyConsumerId) { - case EnergyConsumerId.DISPLAY: - subsystem = MeasuredEnergyArray.SUBSYSTEM_DISPLAY; - break; - default: - continue; - } - subsystems[i] = subsystem; - energyUJ[i] = consumer.energyUWs; - } - return new MeasuredEnergyArray() { - @Override - public int getSubsystem(int index) { - return subsystems[index]; - } - @Override - public long getEnergy(int index) { - return energyUJ[index]; - } - @Override - public int size() { - return size; - } - }; - } - BatteryStatsService(Context context, File systemDir, Handler handler) { // BatteryStatsImpl expects the ActivityManagerService handler, so pass that one through. mContext = context; @@ -253,14 +207,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); - // TODO(b/173077356): Replace directly calling the HAL with PowerStatsService queries - mPowerStatsHALWrapper = PowerStatsHALWrapper.getPowerStatsHalImpl(); - - final MeasuredEnergyArray initialEnergies = getEnergyConsumptionData(); - final boolean[] supportedBuckets = getSupportedEnergyBuckets(initialEnergies); mStats = new BatteryStatsImpl(systemDir, handler, this, - this, supportedBuckets, mUserManagerUserInfoProvider); - mWorker = new BatteryExternalStatsWorker(context, mStats, initialEnergies); + this, mUserManagerUserInfoProvider); + mWorker = new BatteryExternalStatsWorker(context, mStats); mStats.setExternalStatsSyncLocked(mWorker); mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger( com.android.internal.R.integer.config_radioScanningTimeout) * 1000L); @@ -268,30 +217,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats); } - /** - * Map the {@link MeasuredEnergySubsystem}s in the given energyArray to their corresponding - * {@link MeasuredEnergyStats.EnergyBucket}s. - * - * @return array with true for index i if energy bucket i is supported. - */ - private static @Nullable boolean[] getSupportedEnergyBuckets(MeasuredEnergyArray energyArray) { - if (energyArray == null) { - return null; - } - final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS]; - final int size = energyArray.size(); - for (int energyIdx = 0; energyIdx < size; energyIdx++) { - switch (energyArray.getSubsystem(energyIdx)) { - case MeasuredEnergyArray.SUBSYSTEM_DISPLAY: - buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON] = true; - buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE] = true; - buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_OTHER] = true; - break; - } - } - return buckets; - } - public void publish() { LocalServices.addService(BatteryStatsInternal.class, new LocalService()); ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder()); @@ -299,6 +224,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void systemServicesReady() { mStats.systemServicesReady(mContext); + mWorker.systemServicesReady(); Watchdog.getInstance().addMonitor(this); } @@ -2270,7 +2196,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub in.setDataPosition(0); BatteryStatsImpl checkinStats = new BatteryStatsImpl( null, mStats.mHandler, null, null, - null /* energy buckets not currently in checkin anyway */, mUserManagerUserInfoProvider); checkinStats.readSummaryFromParcel(in); in.recycle(); @@ -2311,7 +2236,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub in.setDataPosition(0); BatteryStatsImpl checkinStats = new BatteryStatsImpl( null, mStats.mHandler, null, null, - null /* energy buckets not currently in checkin anyway */, mUserManagerUserInfoProvider); checkinStats.readSummaryFromParcel(in); in.recycle(); diff --git a/services/core/java/com/android/server/am/CacheOomRanker.java b/services/core/java/com/android/server/am/CacheOomRanker.java new file mode 100644 index 000000000000..26cfd62d40ad --- /dev/null +++ b/services/core/java/com/android/server/am/CacheOomRanker.java @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import android.provider.DeviceConfig; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.concurrent.Executor; + +/** + * Class to re-rank a number of the least recently used processes before they + * are assigned oom adjust scores. + */ +public class CacheOomRanker { + @VisibleForTesting + static final String KEY_USE_OOM_RE_RANKING = "use_oom_re_ranking"; + private static final boolean DEFAULT_USE_OOM_RE_RANKING = false; + @VisibleForTesting + static final String KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK = "oom_re_ranking_number_to_re_rank"; + @VisibleForTesting static final int DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK = 8; + @VisibleForTesting + static final String KEY_OOM_RE_RANKING_LRU_WEIGHT = "oom_re_ranking_lru_weight"; + @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_LRU_WEIGHT = 0.35f; + @VisibleForTesting + static final String KEY_OOM_RE_RANKING_USES_WEIGHT = "oom_re_ranking_uses_weight"; + @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_USES_WEIGHT = 0.5f; + @VisibleForTesting + static final String KEY_OOM_RE_RANKING_RSS_WEIGHT = "oom_re_ranking_rss_weight"; + @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_RSS_WEIGHT = 0.15f; + + private static final Comparator<RankedProcessRecord> SCORED_PROCESS_RECORD_COMPARATOR = + new ScoreComparator(); + private static final Comparator<RankedProcessRecord> CACHE_USE_COMPARATOR = + new CacheUseComparator(); + private static final Comparator<RankedProcessRecord> LAST_RSS_COMPARATOR = + new LastRssComparator(); + private static final Comparator<RankedProcessRecord> LAST_ACTIVITY_TIME_COMPARATOR = + new LastActivityTimeComparator(); + + private final Object mPhenotypeFlagLock = new Object(); + + @GuardedBy("mPhenotypeFlagLock") + private boolean mUseOomReRanking = DEFAULT_USE_OOM_RE_RANKING; + // Weight to apply to the LRU ordering. + @GuardedBy("mPhenotypeFlagLock") + @VisibleForTesting float mLruWeight = DEFAULT_OOM_RE_RANKING_LRU_WEIGHT; + // Weight to apply to the ordering by number of times the process has been added to the cache. + @GuardedBy("mPhenotypeFlagLock") + @VisibleForTesting float mUsesWeight = DEFAULT_OOM_RE_RANKING_USES_WEIGHT; + // Weight to apply to the ordering by RSS used by the processes. + @GuardedBy("mPhenotypeFlagLock") + @VisibleForTesting float mRssWeight = DEFAULT_OOM_RE_RANKING_RSS_WEIGHT; + + // Positions to replace in the lru list. + @GuardedBy("mPhenotypeFlagLock") + private int[] mLruPositions; + // Processes to re-rank + @GuardedBy("mPhenotypeFlagLock") + private RankedProcessRecord[] mScoredProcessRecords; + + private final DeviceConfig.OnPropertiesChangedListener mOnFlagsChangedListener = + new DeviceConfig.OnPropertiesChangedListener() { + @Override + public void onPropertiesChanged(DeviceConfig.Properties properties) { + synchronized (mPhenotypeFlagLock) { + for (String name : properties.getKeyset()) { + if (KEY_USE_OOM_RE_RANKING.equals(name)) { + updateUseOomReranking(); + } else if (KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK.equals(name)) { + updateNumberToReRank(); + } else if (KEY_OOM_RE_RANKING_LRU_WEIGHT.equals(name)) { + updateLruWeight(); + } else if (KEY_OOM_RE_RANKING_USES_WEIGHT.equals(name)) { + updateUsesWeight(); + } else if (KEY_OOM_RE_RANKING_RSS_WEIGHT.equals(name)) { + updateRssWeight(); + } + } + } + } + }; + + /** Load settings from device config and register a listener for changes. */ + public void init(Executor executor) { + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + executor, mOnFlagsChangedListener); + synchronized (mPhenotypeFlagLock) { + updateUseOomReranking(); + updateNumberToReRank(); + updateLruWeight(); + updateUsesWeight(); + updateRssWeight(); + } + } + + /** + * Returns whether oom re-ranking is enabled. + */ + public boolean useOomReranking() { + synchronized (mPhenotypeFlagLock) { + return mUseOomReRanking; + } + } + + @GuardedBy("mPhenotypeFlagLock") + private void updateUseOomReranking() { + mUseOomReRanking = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_USE_OOM_RE_RANKING, DEFAULT_USE_OOM_RE_RANKING); + } + + @GuardedBy("mPhenotypeFlagLock") + private void updateNumberToReRank() { + int previousNumberToReRank = getNumberToReRank(); + int numberToReRank = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK, DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK); + if (previousNumberToReRank != numberToReRank) { + mScoredProcessRecords = new RankedProcessRecord[numberToReRank]; + for (int i = 0; i < mScoredProcessRecords.length; ++i) { + mScoredProcessRecords[i] = new RankedProcessRecord(); + } + mLruPositions = new int[numberToReRank]; + } + } + + @GuardedBy("mPhenotypeFlagLock") + @VisibleForTesting + int getNumberToReRank() { + return mScoredProcessRecords == null ? 0 : mScoredProcessRecords.length; + } + + @GuardedBy("mPhenotypeFlagLock") + private void updateLruWeight() { + mLruWeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_OOM_RE_RANKING_LRU_WEIGHT, DEFAULT_OOM_RE_RANKING_LRU_WEIGHT); + } + + @GuardedBy("mPhenotypeFlagLock") + private void updateUsesWeight() { + mUsesWeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_OOM_RE_RANKING_USES_WEIGHT, DEFAULT_OOM_RE_RANKING_USES_WEIGHT); + } + + @GuardedBy("mPhenotypeFlagLock") + private void updateRssWeight() { + mRssWeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_OOM_RE_RANKING_RSS_WEIGHT, DEFAULT_OOM_RE_RANKING_RSS_WEIGHT); + } + + /** + * Re-rank the cached processes in the lru list with a weighted ordering + * of lru, rss size and number of times the process has been put in the cache. + */ + public void reRankLruCachedApps(ProcessList processList) { + float lruWeight; + float usesWeight; + float rssWeight; + int[] lruPositions; + RankedProcessRecord[] scoredProcessRecords; + + ArrayList<ProcessRecord> lruList = processList.mLruProcesses; + + synchronized (mPhenotypeFlagLock) { + lruWeight = mLruWeight; + usesWeight = mUsesWeight; + rssWeight = mRssWeight; + lruPositions = mLruPositions; + scoredProcessRecords = mScoredProcessRecords; + } + + // Don't re-rank if the class hasn't been initialized with defaults. + if (lruPositions == null || scoredProcessRecords == null) { + return; + } + + // Collect the least recently used processes to re-rank, only rank cached + // processes further down the list than mLruProcessServiceStart. + int cachedProcessPos = 0; + for (int i = 0; i < processList.mLruProcessServiceStart + && cachedProcessPos < scoredProcessRecords.length; ++i) { + ProcessRecord app = lruList.get(i); + // Processes that will be assigned a cached oom adj score. + if (!app.killedByAm && app.thread != null && app.curAdj + >= ProcessList.UNKNOWN_ADJ) { + scoredProcessRecords[cachedProcessPos].proc = app; + scoredProcessRecords[cachedProcessPos].score = 0.0f; + lruPositions[cachedProcessPos] = i; + ++cachedProcessPos; + } + } + + // TODO maybe ensure a certain number above this in the cache before re-ranking. + if (cachedProcessPos < scoredProcessRecords.length) { + // Ignore we don't have enough processes to worry about re-ranking. + return; + } + + // Add scores for each of the weighted features we want to rank based on. + if (lruWeight > 0.0f) { + // This doesn't use the LRU list ordering as after the first re-ranking + // that will no longer be lru. + Arrays.sort(scoredProcessRecords, LAST_ACTIVITY_TIME_COMPARATOR); + addToScore(scoredProcessRecords, lruWeight); + } + if (rssWeight > 0.0f) { + Arrays.sort(scoredProcessRecords, LAST_RSS_COMPARATOR); + addToScore(scoredProcessRecords, rssWeight); + } + if (usesWeight > 0.0f) { + Arrays.sort(scoredProcessRecords, CACHE_USE_COMPARATOR); + addToScore(scoredProcessRecords, usesWeight); + } + + // Re-rank by the new combined score. + Arrays.sort(scoredProcessRecords, SCORED_PROCESS_RECORD_COMPARATOR); + + if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) { + boolean printedHeader = false; + for (int i = 0; i < scoredProcessRecords.length; ++i) { + if (scoredProcessRecords[i].proc.pid != lruList.get(lruPositions[i]).pid) { + if (!printedHeader) { + Slog.i(OomAdjuster.TAG, "reRankLruCachedApps"); + printedHeader = true; + } + Slog.i(OomAdjuster.TAG, " newPos=" + lruPositions[i] + " " + + scoredProcessRecords[i].proc); + } + } + } + + for (int i = 0; i < scoredProcessRecords.length; ++i) { + lruList.set(lruPositions[i], scoredProcessRecords[i].proc); + scoredProcessRecords[i].proc = null; + } + } + + private static void addToScore(RankedProcessRecord[] scores, float weight) { + for (int i = 1; i < scores.length; ++i) { + scores[i].score += i * weight; + } + } + + void dump(PrintWriter pw) { + pw.println("CacheOomRanker settings"); + synchronized (mPhenotypeFlagLock) { + pw.println(" " + KEY_USE_OOM_RE_RANKING + "=" + mUseOomReRanking); + pw.println(" " + KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK + "=" + getNumberToReRank()); + pw.println(" " + KEY_OOM_RE_RANKING_LRU_WEIGHT + "=" + mLruWeight); + pw.println(" " + KEY_OOM_RE_RANKING_USES_WEIGHT + "=" + mUsesWeight); + pw.println(" " + KEY_OOM_RE_RANKING_RSS_WEIGHT + "=" + mRssWeight); + } + } + + private static class ScoreComparator implements Comparator<RankedProcessRecord> { + @Override + public int compare(RankedProcessRecord o1, RankedProcessRecord o2) { + return Float.compare(o1.score, o2.score); + } + } + + private static class LastActivityTimeComparator implements Comparator<RankedProcessRecord> { + @Override + public int compare(RankedProcessRecord o1, RankedProcessRecord o2) { + return Long.compare(o1.proc.lastActivityTime, o2.proc.lastActivityTime); + } + } + + private static class CacheUseComparator implements Comparator<RankedProcessRecord> { + @Override + public int compare(RankedProcessRecord o1, RankedProcessRecord o2) { + return Long.compare(o1.proc.getCacheOomRankerUseCount(), + o2.proc.getCacheOomRankerUseCount()); + } + } + + private static class LastRssComparator implements Comparator<RankedProcessRecord> { + @Override + public int compare(RankedProcessRecord o1, RankedProcessRecord o2) { + // High RSS first to match least recently used. + return Long.compare(o2.proc.mLastRss, o1.proc.mLastRss); + } + } + + private static class RankedProcessRecord { + public ProcessRecord proc; + public float score; + } +} diff --git a/services/core/java/com/android/server/am/FgsStartTempAllowList.java b/services/core/java/com/android/server/am/FgsStartTempAllowList.java index 1f9039392405..3aca4cfd0162 100644 --- a/services/core/java/com/android/server/am/FgsStartTempAllowList.java +++ b/services/core/java/com/android/server/am/FgsStartTempAllowList.java @@ -28,7 +28,7 @@ import android.util.SparseLongArray; final class FgsStartTempAllowList { private static final int MAX_SIZE = 100; /** - * The key is the UID, the value is expiration elapse time in ms of this temp-allowed UID. + * The key is the uid, the value is expiration elapse time in ms of this temp-allowed uid. */ private final SparseLongArray mTempAllowListFgs = new SparseLongArray(); @@ -37,7 +37,8 @@ final class FgsStartTempAllowList { void add(int uid, long duration) { if (duration <= 0) { - Slog.e(TAG_AM, "FgsStartTempAllowList bad duration:" + duration + " uid: " + uid); + Slog.e(TAG_AM, "FgsStartTempAllowList bad duration:" + duration + " uid: " + + uid); return; } // The temp allowlist should be a short list with only a few entries in it. diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index e5d57df742aa..de7931535297 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -72,6 +72,7 @@ import static com.android.server.am.AppProfiler.TAG_PSS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; import android.app.ActivityManager; +import android.app.ActivityThread; import android.app.ApplicationExitInfo; import android.app.usage.UsageEvents; import android.compat.annotation.ChangeId; @@ -91,7 +92,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.Trace; -import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.LongSparseArray; @@ -119,7 +119,7 @@ import java.util.Arrays; * All of the code required to compute proc states and oom_adj values. */ public final class OomAdjuster { - private static final String TAG = "OomAdjuster"; + static final String TAG = "OomAdjuster"; static final String OOM_ADJ_REASON_METHOD = "updateOomAdj"; static final String OOM_ADJ_REASON_NONE = OOM_ADJ_REASON_METHOD + "_meh"; static final String OOM_ADJ_REASON_ACTIVITY = OOM_ADJ_REASON_METHOD + "_activityChange"; @@ -169,6 +169,12 @@ public final class OomAdjuster { */ CachedAppOptimizer mCachedAppOptimizer; + /** + * Re-rank apps getting a cache oom adjustment from lru to weighted order + * based on weighted scores for LRU, PSS and cache use count. + */ + CacheOomRanker mCacheOomRanker; + ActivityManagerConstants mConstants; final long[] mTmpLong = new long[3]; @@ -331,6 +337,7 @@ public final class OomAdjuster { mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class); mConstants = mService.mConstants; mCachedAppOptimizer = new CachedAppOptimizer(mService); + mCacheOomRanker = new CacheOomRanker(); mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> { final int pid = msg.arg1; @@ -361,6 +368,7 @@ public final class OomAdjuster { void initSettings() { mCachedAppOptimizer.init(); + mCacheOomRanker.init(ActivityThread.currentApplication().getMainExecutor()); if (mService.mConstants.KEEP_WARMING_SERVICES.size() > 0) { final IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED); mService.mContext.registerReceiverForAllUsers(new BroadcastReceiver() { @@ -769,6 +777,9 @@ public final class OomAdjuster { } } + if (mCacheOomRanker.useOomReranking()) { + mCacheOomRanker.reRankLruCachedApps(mProcessList); + } assignCachedAdjIfNecessary(mProcessList.mLruProcesses); if (computeClients) { // There won't be cycles if we didn't compute clients above. @@ -2719,11 +2730,11 @@ public final class OomAdjuster { } @GuardedBy("mService") - final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) { + final void setAppIdTempWhitelistStateLocked(int uid, boolean onWhitelist) { boolean changed = false; for (int i = mActiveUids.size() - 1; i >= 0; i--) { final UidRecord uidRec = mActiveUids.valueAt(i); - if (UserHandle.getAppId(uidRec.uid) == appId && uidRec.curWhitelist != onWhitelist) { + if (uidRec.uid == uid && uidRec.curWhitelist != onWhitelist) { uidRec.curWhitelist = onWhitelist; changed = true; } @@ -2775,6 +2786,11 @@ public final class OomAdjuster { } @GuardedBy("mService") + void dumpCacheOomRankerSettings(PrintWriter pw) { + mCacheOomRanker.dump(pw); + } + + @GuardedBy("mService") void updateAppFreezeStateLocked(ProcessRecord app) { if (!mCachedAppOptimizer.useFreezer()) { return; diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index d4d01652e338..520a28bfd889 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -360,6 +360,13 @@ class ProcessRecord implements WindowProcessListener { int mCachedProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY; int mCachedSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND; + // Approximates the usage count of the app, used for cache re-ranking by CacheOomRanker. + // + // Counts the number of times the process is re-added to the cache (i.e. setCached(false); + // setCached(true)). This over counts, as setCached is sometimes reset while remaining in the + // cache. However, this happens uniformly across processes, so ranking is not affected. + private int mCacheOomRankerUseCount; + boolean mReachable; // Whether or not this process is reachable from given process long mKillTime; // The timestamp in uptime when this process was killed. @@ -828,7 +835,12 @@ class ProcessRecord implements WindowProcessListener { } void setCached(boolean cached) { - mCached = cached; + if (mCached != cached) { + mCached = cached; + if (cached) { + ++mCacheOomRankerUseCount; + } + } } @Override @@ -836,6 +848,10 @@ class ProcessRecord implements WindowProcessListener { return mCached; } + int getCacheOomRankerUseCount() { + return mCacheOomRankerUseCount; + } + boolean hasActivities() { return mWindowProcessController.hasActivities(); } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index c04f6ff37229..d73de7c309f0 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -628,19 +628,30 @@ class UserController implements Handler.Callback { Binder.getCallingUid(), Binder.getCallingPid(), userId); } - if (getUserInfo(userId).isManagedProfile()) { + final UserInfo userInfo = getUserInfo(userId); + if (userInfo.isProfile()) { UserInfo parent = mInjector.getUserManager().getProfileParent(userId); if (parent != null) { - final Intent profileUnlockedIntent = new Intent( - Intent.ACTION_MANAGED_PROFILE_UNLOCKED); - profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); - profileUnlockedIntent.addFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY - | Intent.FLAG_RECEIVER_FOREGROUND); - mInjector.broadcastIntent(profileUnlockedIntent, - null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), - Binder.getCallingPid(), parent.id); + // Send PROFILE_ACCESSIBLE broadcast to the parent user if a profile was unlocked + broadcastProfileAccessibleStateChanged(userId, parent.id, + Intent.ACTION_PROFILE_ACCESSIBLE); + + //TODO(b/175704931): send ACTION_MANAGED_PROFILE_AVAILABLE + + // Also send MANAGED_PROFILE_UNLOCKED broadcast to the parent user + // if a managed profile was unlocked + if (userInfo.isManagedProfile()) { + final Intent profileUnlockedIntent = new Intent( + Intent.ACTION_MANAGED_PROFILE_UNLOCKED); + profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); + profileUnlockedIntent.addFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY + | Intent.FLAG_RECEIVER_FOREGROUND); + mInjector.broadcastIntent(profileUnlockedIntent, + null, null, 0, null, null, null, AppOpsManager.OP_NONE, + null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), parent.id); + } } } @@ -761,13 +772,44 @@ class UserController implements Handler.Callback { int restartUser(final int userId, final boolean foreground) { return stopUser(userId, /* force= */ true, /* allowDelayedLocking= */ false, /* stopUserCallback= */ null, new KeyEvictedCallback() { - @Override - public void keyEvicted(@UserIdInt int userId) { - // Post to the same handler that this callback is called from to ensure the user - // cleanup is complete before restarting. - mHandler.post(() -> UserController.this.startUser(userId, foreground)); - } - }); + @Override + public void keyEvicted(@UserIdInt int userId) { + // Post to the same handler that this callback is called from to ensure + // the user cleanup is complete before restarting. + mHandler.post(() -> UserController.this.startUser(userId, foreground)); + } + }); + } + + /** + * Stops a user only if it's a profile, with a more relaxed permission requirement: + * {@link android.Manifest.permission#MANAGE_USERS} or + * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. + * To be called from ActivityManagerService. + * @param userId the id of the user to stop. + * @return true if the operation was successful. + */ + boolean stopProfile(final @UserIdInt int userId) { + if (mInjector.checkCallingPermission(android.Manifest.permission.MANAGE_USERS) + == PackageManager.PERMISSION_DENIED && mInjector.checkCallingPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + == PackageManager.PERMISSION_DENIED) { + throw new SecurityException( + "You either need MANAGE_USERS or INTERACT_ACROSS_USERS_FULL permission to " + + "stop a profile"); + } + + final UserInfo userInfo = getUserInfo(userId); + if (userInfo == null || !userInfo.isProfile()) { + throw new IllegalArgumentException("User " + userId + " is not a profile"); + } + + enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId); + synchronized (mLock) { + return stopUsersLU(userId, /* force= */ true, /* allowDelayedLocking= */ + false, /* stopUserCallback= */ null, /* keyEvictedCallback= */ null) + == ActivityManager.USER_OP_SUCCESS; + } } int stopUser(final int userId, final boolean force, boolean allowDelayedLocking, @@ -1144,6 +1186,17 @@ class UserController implements Handler.Callback { null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL); + + // Send PROFILE_INACCESSIBLE broadcast if a profile was stopped + final UserInfo userInfo = getUserInfo(userId); + if (userInfo.isProfile()) { + UserInfo parent = mInjector.getUserManager().getProfileParent(userId); + if (parent != null) { + broadcastProfileAccessibleStateChanged(userId, parent.id, + Intent.ACTION_PROFILE_INACCESSIBLE); + //TODO(b/175704931): send ACTION_MANAGED_PROFILE_UNAVAILABLE + } + } } /** @@ -1208,6 +1261,37 @@ class UserController implements Handler.Callback { } } + /** + * Starts a user only if it's a profile, with a more relaxed permission requirement: + * {@link android.Manifest.permission#MANAGE_USERS} or + * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. + * To be called from ActivityManagerService. + * @param userId the id of the user to start. + * @return true if the operation was successful. + */ + boolean startProfile(final @UserIdInt int userId) { + if (mInjector.checkCallingPermission(android.Manifest.permission.MANAGE_USERS) + == PackageManager.PERMISSION_DENIED && mInjector.checkCallingPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + == PackageManager.PERMISSION_DENIED) { + throw new SecurityException( + "You either need MANAGE_USERS or INTERACT_ACROSS_USERS_FULL permission to " + + "start a profile"); + } + + final UserInfo userInfo = getUserInfo(userId); + if (userInfo == null || !userInfo.isProfile()) { + throw new IllegalArgumentException("User " + userId + " is not a profile"); + } + + if (!userInfo.isEnabled()) { + Slog.w(TAG, "Cannot start disabled profile #" + userId); + return false; + } + + return startUserNoChecks(userId, /* foreground= */ false, /* unlockListener= */ null); + } + boolean startUser(final @UserIdInt int userId, final boolean foreground) { return startUser(userId, foreground, null); } @@ -1248,9 +1332,13 @@ class UserController implements Handler.Callback { final @UserIdInt int userId, final boolean foreground, @Nullable IProgressListener unlockListener) { - checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "startUser"); + return startUserNoChecks(userId, foreground, unlockListener); + } + + private boolean startUserNoChecks(final @UserIdInt int userId, final boolean foreground, + @Nullable IProgressListener unlockListener) { TimingsTraceAndSlog t = new TimingsTraceAndSlog(); t.traceBegin("startUser-" + userId + "-" + (foreground ? "fg" : "bg")); @@ -1872,6 +1960,25 @@ class UserController implements Handler.Callback { } } + /** + * Broadcasts to the parent user when a profile is started+unlocked/stopped. + * @param userId the id of the profile + * @param parentId the id of the parent user + * @param intentAction either ACTION_PROFILE_ACCESSIBLE or ACTION_PROFILE_INACCESSIBLE + */ + private void broadcastProfileAccessibleStateChanged(@UserIdInt int userId, + @UserIdInt int parentId, + String intentAction) { + final Intent intent = new Intent(intentAction); + intent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY + | Intent.FLAG_RECEIVER_FOREGROUND); + mInjector.broadcastIntent(intent, /* resolvedType= */ null, /* resultTo= */ + null, /* resultCode= */ 0, /* resultData= */ null, /* resultExtras= */ + null, /* requiredPermissions= */ null, AppOpsManager.OP_NONE, /* bOptions= */ + null, /* ordered= */ false, /* sticky= */ false, MY_PID, SYSTEM_UID, + Binder.getCallingUid(), Binder.getCallingPid(), parentId); + } int handleIncomingUser(int callingPid, int callingUid, @UserIdInt int userId, boolean allowAll, int allowMode, String name, String callerPackage) { diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java index 676fcd0bdc87..17fd32c57e09 100644 --- a/services/core/java/com/android/server/appop/HistoricalRegistry.java +++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java @@ -1488,7 +1488,7 @@ final class HistoricalRegistry { private void writeHistoricalPackageOpsDLocked(@NonNull HistoricalPackageOps packageOps, @NonNull TypedXmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_PACKAGE); - serializer.attribute(null, ATTR_NAME, packageOps.getPackageName()); + serializer.attributeInterned(null, ATTR_NAME, packageOps.getPackageName()); final int numAttributions = packageOps.getAttributedOpsCount(); for (int i = 0; i < numAttributions; i++) { final AppOpsManager.AttributedHistoricalOps op = packageOps.getAttributedOpsAt(i); diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java index e4d90525fc68..52152ab78992 100644 --- a/services/core/java/com/android/server/biometrics/AuthSession.java +++ b/services/core/java/com/android/server/biometrics/AuthSession.java @@ -805,7 +805,7 @@ public final class AuthSession implements IBinder.DeathRecipient { @Override public String toString() { return "State: " + mState - + "\nisCrypto: " + isCrypto() - + "\nPreAuthInfo: " + mPreAuthInfo; + + ", isCrypto: " + isCrypto() + + ", PreAuthInfo: " + mPreAuthInfo; } } diff --git a/services/core/java/com/android/server/biometrics/BiometricSensor.java b/services/core/java/com/android/server/biometrics/BiometricSensor.java index 17ec11233c4e..85de81bb3491 100644 --- a/services/core/java/com/android/server/biometrics/BiometricSensor.java +++ b/services/core/java/com/android/server/biometrics/BiometricSensor.java @@ -169,12 +169,11 @@ public abstract class BiometricSensor { @Override public String toString() { return "ID(" + id + ")" - + "\n oemStrength: " + oemStrength - + "\n updatedStrength: " + mUpdatedStrength - + "\n modality " + modality - + "\n state: " + mSensorState - + "\n cookie: " + mCookie - + "\n authenticator: " + impl - + "\n"; + + ", oemStrength: " + oemStrength + + ", updatedStrength: " + mUpdatedStrength + + ", modality " + modality + + ", state: " + mSensorState + + ", cookie: " + mCookie + + ", authenticator: " + impl; } } diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index fd5ada0ff9be..3387049d69f3 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -252,12 +252,14 @@ public class BiometricService extends SystemService { @NonNull private final IInvalidationCallback mClientCallback; @NonNull private final Set<Integer> mSensorsPendingInvalidation; - public static InvalidationTracker start(@NonNull ArrayList<BiometricSensor> sensors, + public static InvalidationTracker start(@NonNull Context context, + @NonNull ArrayList<BiometricSensor> sensors, int userId, int fromSensorId, @NonNull IInvalidationCallback clientCallback) { - return new InvalidationTracker(sensors, userId, fromSensorId, clientCallback); + return new InvalidationTracker(context, sensors, userId, fromSensorId, clientCallback); } - private InvalidationTracker(@NonNull ArrayList<BiometricSensor> sensors, int userId, + private InvalidationTracker(@NonNull Context context, + @NonNull ArrayList<BiometricSensor> sensors, int userId, int fromSensorId, @NonNull IInvalidationCallback clientCallback) { mClientCallback = clientCallback; mSensorsPendingInvalidation = new ArraySet<>(); @@ -271,6 +273,14 @@ public class BiometricService extends SystemService { continue; } + try { + if (!sensor.impl.hasEnrolledTemplates(userId, context.getOpPackageName())) { + continue; + } + } catch (RemoteException e) { + Slog.e(TAG, "Remote Exception", e); + } + Slog.d(TAG, "Requesting authenticatorId invalidation for sensor: " + sensor.id); synchronized (this) { @@ -288,6 +298,17 @@ public class BiometricService extends SystemService { Slog.d(TAG, "RemoteException", e); } } + + synchronized (this) { + if (mSensorsPendingInvalidation.isEmpty()) { + try { + Slog.d(TAG, "No sensors require invalidation"); + mClientCallback.onCompleted(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote Exception", e); + } + } + } } @VisibleForTesting @@ -742,7 +763,7 @@ public class BiometricService extends SystemService { IInvalidationCallback callback) { checkInternalPermission(); - InvalidationTracker.start(mSensors, userId, fromSensorId, callback); + InvalidationTracker.start(getContext(), mSensors, userId, fromSensorId, callback); } @Override // Binder call diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java index fa503889f26f..55ac24835b2a 100644 --- a/services/core/java/com/android/server/biometrics/Utils.java +++ b/services/core/java/com/android/server/biometrics/Utils.java @@ -408,7 +408,7 @@ public class Utils { return hasPermission && keyguardPackage != null && keyguardPackage.equals(clientPackage); } - public static String getClientName(@Nullable BaseClientMonitor<?> client) { + public static String getClientName(@Nullable BaseClientMonitor client) { return client != null ? client.getClass().getSimpleName() : "null"; } diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java index c662df252971..b3580fb79042 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java @@ -29,10 +29,10 @@ import android.os.Vibrator; import android.util.Slog; /** - * Abstract {@link BaseClientMonitor} subclass that operations eligible/interested in acquisition + * Abstract {@link HalClientMonitor} subclass that operations eligible/interested in acquisition * messages should extend. */ -public abstract class AcquisitionClient<T> extends BaseClientMonitor<T> implements Interruptable { +public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implements Interruptable { private static final String TAG = "Biometrics/AcquisitionClient"; diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java index 7c9fe38c6add..f3c37efd4b61 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java @@ -31,7 +31,7 @@ import java.util.NoSuchElementException; * the current client. Subclasses are responsible for coordinating the interaction with * the biometric's HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.). */ -public abstract class BaseClientMonitor<T> extends LoggableMonitor +public abstract class BaseClientMonitor extends LoggableMonitor implements IBinder.DeathRecipient { private static final String TAG = "Biometrics/ClientMonitor"; @@ -50,7 +50,7 @@ public abstract class BaseClientMonitor<T> extends LoggableMonitor * * @param clientMonitor Reference of the ClientMonitor that is starting. */ - default void onClientStarted(@NonNull BaseClientMonitor<?> clientMonitor) {} + default void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {} /** * Invoked when the ClientMonitor operation is complete. This abstracts away asynchronous @@ -61,23 +61,11 @@ public abstract class BaseClientMonitor<T> extends LoggableMonitor * @param clientMonitor Reference of the ClientMonitor that finished. * @param success True if the operation completed successfully. */ - default void onClientFinished(@NonNull BaseClientMonitor<?> clientMonitor, - boolean success) {} - } - - /** - * Interface that allows ClientMonitor subclasses to retrieve a fresh instance to the HAL. - */ - public interface LazyDaemon<T> { - /** - * @return A fresh instance to the biometric HAL - */ - T getDaemon(); + default void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {} } protected final int mSequentialId; @NonNull private final Context mContext; - @NonNull protected final LazyDaemon<T> mLazyDaemon; private final int mTargetUserId; @NonNull private final String mOwner; private final int mSensorId; // sensorId as configured by the framework @@ -93,7 +81,6 @@ public abstract class BaseClientMonitor<T> extends LoggableMonitor /** * @param context system_server context - * @param lazyDaemon pointer for lazy retrieval of the HAL * @param token a unique token for the client * @param listener recipient of related events (e.g. authentication) * @param userId target user id for operation @@ -104,14 +91,13 @@ public abstract class BaseClientMonitor<T> extends LoggableMonitor * @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants * @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants */ - public BaseClientMonitor(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, + public BaseClientMonitor(@NonNull Context context, @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction, int statsClient) { super(statsModality, statsAction, statsClient); mSequentialId = sCount++; mContext = context; - mLazyDaemon = lazyDaemon; mToken = token; mListener = listener; mTargetUserId = userId; @@ -133,15 +119,7 @@ public abstract class BaseClientMonitor<T> extends LoggableMonitor } /** - * Invoked if the scheduler is unable to start the ClientMonitor (for example the HAL is null). - * If such a problem is detected, the scheduler will not invoke - * {@link #start(Callback)}. - */ - public abstract void unableToStart(); - - /** - * Starts the ClientMonitor's lifecycle. Invokes {@link #startHalOperation()} when internal book - * keeping is complete. + * Starts the ClientMonitor's lifecycle. * @param callback invoked when the operation is complete (succeeds, fails, etc) */ public void start(@NonNull Callback callback) { @@ -149,10 +127,6 @@ public abstract class BaseClientMonitor<T> extends LoggableMonitor mCallback.onClientStarted(this); } - /** - * Starts the HAL operation specific to the ClientMonitor subclass. - */ - protected abstract void startHalOperation(); public boolean isAlreadyDone() { return mAlreadyDone; @@ -221,10 +195,6 @@ public abstract class BaseClientMonitor<T> extends LoggableMonitor return mSensorId; } - public T getFreshDaemon() { - return mLazyDaemon.getDaemon(); - } - @Override public String toString() { return "{[" + mSequentialId + "] " diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index fecc6f0fa45e..aa7faf51b1b6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -101,17 +101,34 @@ public class BiometricScheduler { @Retention(RetentionPolicy.SOURCE) @interface OperationState {} - @NonNull final BaseClientMonitor<?> mClientMonitor; + @NonNull final BaseClientMonitor mClientMonitor; @Nullable final BaseClientMonitor.Callback mClientCallback; @OperationState int mState; - Operation(@NonNull BaseClientMonitor<?> clientMonitor, + Operation(@NonNull BaseClientMonitor clientMonitor, @Nullable BaseClientMonitor.Callback callback) { this.mClientMonitor = clientMonitor; this.mClientCallback = callback; mState = STATE_WAITING_IN_QUEUE; } + public boolean isHalOperation() { + return mClientMonitor instanceof HalClientMonitor<?>; + } + + /** + * @return true if the operation requires the HAL, and the HAL is null. + */ + public boolean isUnstartableHalOperation() { + if (isHalOperation()) { + final HalClientMonitor<?> client = (HalClientMonitor<?>) mClientMonitor; + if (client.getFreshDaemon() == null) { + return true; + } + } + return false; + } + @Override public String toString() { return mClientMonitor + ", State: " + mState; @@ -188,7 +205,7 @@ public class BiometricScheduler { // starting the next client). public class InternalCallback implements BaseClientMonitor.Callback { @Override - public void onClientStarted(@NonNull BaseClientMonitor<?> clientMonitor) { + public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { Slog.d(getTag(), "[Started] " + clientMonitor); if (mCurrentOperation.mClientCallback != null) { mCurrentOperation.mClientCallback.onClientStarted(clientMonitor); @@ -196,7 +213,7 @@ public class BiometricScheduler { } @Override - public void onClientFinished(@NonNull BaseClientMonitor<?> clientMonitor, boolean success) { + public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { mHandler.post(() -> { if (mCurrentOperation == null) { Slog.e(getTag(), "[Finishing] " + clientMonitor @@ -276,7 +293,7 @@ public class BiometricScheduler { } mCurrentOperation = mPendingOperations.poll(); - final BaseClientMonitor<?> currentClient = mCurrentOperation.mClientMonitor; + final BaseClientMonitor currentClient = mCurrentOperation.mClientMonitor; // If the operation at the front of the queue has been marked for cancellation, send // ERROR_CANCELED. No need to start this client. @@ -305,7 +322,9 @@ public class BiometricScheduler { // to arrive at the head of the queue, before pinging it to start. final boolean shouldStartNow = currentClient.getCookie() == 0; if (shouldStartNow) { - if (mCurrentOperation.mClientMonitor.getFreshDaemon() == null) { + if (mCurrentOperation.isUnstartableHalOperation()) { + final HalClientMonitor<?> halClientMonitor = + (HalClientMonitor<?>) mCurrentOperation.mClientMonitor; // Note down current length of queue final int pendingOperationsLength = mPendingOperations.size(); final Operation lastOperation = mPendingOperations.peekLast(); @@ -315,7 +334,7 @@ public class BiometricScheduler { // For current operations, 1) unableToStart, which notifies the caller-side, then // 2) notify operation's callback, to notify applicable system service that the // operation failed. - mCurrentOperation.mClientMonitor.unableToStart(); + halClientMonitor.unableToStart(); if (mCurrentOperation.mClientCallback != null) { mCurrentOperation.mClientCallback.onClientFinished( mCurrentOperation.mClientMonitor, false /* success */); @@ -331,7 +350,9 @@ public class BiometricScheduler { + ", expected length: " + pendingOperationsLength); break; } - operation.mClientMonitor.unableToStart(); + if (operation.isHalOperation()) { + ((HalClientMonitor<?>) operation.mClientMonitor).unableToStart(); + } if (operation.mClientCallback != null) { operation.mClientCallback.onClientFinished(operation.mClientMonitor, false /* success */); @@ -401,10 +422,12 @@ public class BiometricScheduler { return; } - if (mCurrentOperation.mClientMonitor.getFreshDaemon() == null) { + if (mCurrentOperation.isUnstartableHalOperation()) { Slog.e(getTag(), "[Unable To Start] Prepared client: " + mCurrentOperation); // This is BiometricPrompt trying to auth but something's wrong with the HAL. - mCurrentOperation.mClientMonitor.unableToStart(); + final HalClientMonitor<?> halClientMonitor = + (HalClientMonitor<?>) mCurrentOperation.mClientMonitor; + halClientMonitor.unableToStart(); if (mCurrentOperation.mClientCallback != null) { mCurrentOperation.mClientCallback.onClientFinished(mCurrentOperation.mClientMonitor, false /* success */); @@ -423,7 +446,7 @@ public class BiometricScheduler { * * @param clientMonitor operation to be scheduled */ - public void scheduleClientMonitor(@NonNull BaseClientMonitor<?> clientMonitor) { + public void scheduleClientMonitor(@NonNull BaseClientMonitor clientMonitor) { scheduleClientMonitor(clientMonitor, null /* clientFinishCallback */); } @@ -434,7 +457,7 @@ public class BiometricScheduler { * @param clientCallback optional callback, invoked when the client is finished, but * before it has been removed from the queue. */ - public void scheduleClientMonitor(@NonNull BaseClientMonitor<?> clientMonitor, + public void scheduleClientMonitor(@NonNull BaseClientMonitor clientMonitor, @Nullable BaseClientMonitor.Callback clientCallback) { // Mark any interruptable pending clients as canceling. Once they reach the head of the // queue, the scheduler will send ERROR_CANCELED and skip the operation. @@ -537,7 +560,7 @@ public class BiometricScheduler { /** * @return the current operation */ - public BaseClientMonitor<?> getCurrentClient() { + public BaseClientMonitor getCurrentClient() { if (mCurrentOperation == null) { return null; } diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java index 7f01fdac608f..6a622c339d0b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java @@ -23,7 +23,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; -public abstract class GenerateChallengeClient<T> extends BaseClientMonitor<T> { +public abstract class GenerateChallengeClient<T> extends HalClientMonitor<T> { private static final String TAG = "GenerateChallengeClient"; diff --git a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java new file mode 100644 index 000000000000..63cd4125262d --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.os.IBinder; + +/** + * Abstract {@link BaseClientMonitor} implementation that supports HAL operations. + * @param <T> HAL template + */ +public abstract class HalClientMonitor<T> extends BaseClientMonitor { + /** + * Interface that allows ClientMonitor subclasses to retrieve a fresh instance to the HAL. + */ + public interface LazyDaemon<T> { + /** + * @return A fresh instance to the biometric HAL + */ + T getDaemon(); + } + + /** + * Starts the HAL operation specific to the ClientMonitor subclass. + */ + protected abstract void startHalOperation(); + + /** + * Invoked if the scheduler is unable to start the ClientMonitor (for example the HAL is null). + * If such a problem is detected, the scheduler will not invoke + * {@link #start(Callback)}. + */ + public abstract void unableToStart(); + + @NonNull + protected final LazyDaemon<T> mLazyDaemon; + + /** + * @param context system_server context + * @param lazyDaemon pointer for lazy retrieval of the HAL + * @param token a unique token for the client + * @param listener recipient of related events (e.g. authentication) + * @param userId target user id for operation + * @param owner name of the client that owns this + * @param cookie BiometricPrompt authentication cookie (to be moved into a subclass soon) + * @param sensorId ID of the sensor that the operation should be requested of + * @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants + * @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants + * @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants + */ + public HalClientMonitor(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, + @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId, + @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction, + int statsClient) { + super(context, token, listener, userId, owner, cookie, sensorId, statsModality, + statsAction, statsClient); + mLazyDaemon = lazyDaemon; + } + + @Nullable + public T getFreshDaemon() { + return mLazyDaemon.getDaemon(); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java index edc7edccaf7b..8529e810f9a1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java @@ -39,7 +39,7 @@ import java.util.Map; * {@link #onRemoved(BiometricAuthenticator.Identifier, int)} returns true/ */ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Identifier, T> - extends BaseClientMonitor<T> implements EnumerateConsumer, RemovalConsumer { + extends HalClientMonitor<T> implements EnumerateConsumer, RemovalConsumer { private static final String TAG = "Biometrics/InternalCleanupClient"; @@ -60,11 +60,11 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide private final BiometricUtils<S> mBiometricUtils; private final Map<Integer, Long> mAuthenticatorIds; private final List<S> mEnrolledList; - private BaseClientMonitor<T> mCurrentTask; + private BaseClientMonitor mCurrentTask; private final Callback mEnumerateCallback = new Callback() { @Override - public void onClientFinished(@NonNull BaseClientMonitor<?> clientMonitor, boolean success) { + public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { final List<BiometricAuthenticator.Identifier> unknownHALTemplates = ((InternalEnumerateClient<T>) mCurrentTask).getUnknownHALTemplates(); @@ -88,7 +88,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide private final Callback mRemoveCallback = new Callback() { @Override - public void onClientFinished(@NonNull BaseClientMonitor<?> clientMonitor, boolean success) { + public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { mCallback.onClientFinished(InternalCleanupClient.this, success); } }; diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java index 176e49c15716..2693f2f0e2de 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java @@ -31,7 +31,7 @@ import java.util.List; /** * Internal class to help clean up unknown templates in the HAL and Framework */ -public abstract class InternalEnumerateClient<T> extends BaseClientMonitor<T> +public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T> implements EnumerateConsumer { private static final String TAG = "Biometrics/InternalEnumerateClient"; diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java index 93728d0a8c47..630e5eaae377 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java @@ -31,7 +31,7 @@ import java.util.Map; * {@link InvalidationRequesterClient} for more info. */ public abstract class InvalidationClient<S extends BiometricAuthenticator.Identifier, T> - extends BaseClientMonitor<T> { + extends HalClientMonitor<T> { private static final String TAG = "InvalidationClient"; diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java index e16b2f81bc5f..c97003b875ed 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java @@ -55,8 +55,8 @@ import android.hardware.biometrics.IInvalidationCallback; * switches, the framework can check if any sensor has the "invalidationInProgress" flag set. If so, * the framework should re-start the invalidation process described above. */ -public abstract class InvalidationRequesterClient<S extends BiometricAuthenticator.Identifier, T> - extends BaseClientMonitor<T> { +public class InvalidationRequesterClient<S extends BiometricAuthenticator.Identifier> + extends BaseClientMonitor { private final BiometricManager mBiometricManager; @NonNull private final BiometricUtils<S> mUtils; @@ -71,9 +71,9 @@ public abstract class InvalidationRequesterClient<S extends BiometricAuthenticat } }; - public InvalidationRequesterClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, - int userId, int sensorId, @NonNull BiometricUtils<S> utils) { - super(context, lazyDaemon, null /* token */, null /* listener */, userId, + public InvalidationRequesterClient(@NonNull Context context, int userId, int sensorId, + @NonNull BiometricUtils<S> utils) { + super(context, null /* token */, null /* listener */, userId, context.getOpPackageName(), 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); @@ -89,14 +89,4 @@ public abstract class InvalidationRequesterClient<S extends BiometricAuthenticat mBiometricManager.invalidateAuthenticatorIds(getTargetUserId(), getSensorId(), mInvalidationCallback); } - - @Override - public void unableToStart() { - - } - - @Override - protected void startHalOperation() { - // No HAL operations necessary - } } diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java index cec6dde751a6..4ea48fdf05a0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java @@ -31,7 +31,7 @@ import java.util.Map; * A class to keep track of the remove state for a given client. */ public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier, T> - extends BaseClientMonitor<T> implements RemovalConsumer { + extends HalClientMonitor<T> implements RemovalConsumer { private static final String TAG = "Biometrics/RemovalClient"; diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java index 35cbcc00ee44..187193dcd50a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java @@ -21,7 +21,7 @@ import android.content.Context; import android.hardware.biometrics.BiometricsProtoEnums; import android.os.IBinder; -public abstract class RevokeChallengeClient<T> extends BaseClientMonitor<T> { +public abstract class RevokeChallengeClient<T> extends HalClientMonitor<T> { public RevokeChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, @NonNull IBinder token, @NonNull String owner, int sensorId) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java index acf0f08096aa..5fb194c75d77 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java @@ -23,11 +23,11 @@ import android.hardware.biometrics.face.ISession; import android.os.RemoteException; import android.util.Slog; -import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.HalClientMonitor; import java.util.Map; -class FaceGetAuthenticatorIdClient extends BaseClientMonitor<ISession> { +class FaceGetAuthenticatorIdClient extends HalClientMonitor<ISession> { private static final String TAG = "FaceGetAuthenticatorIdClient"; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index 058bcf669a3a..810489b1c740 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -46,6 +46,8 @@ import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.HalClientMonitor; +import com.android.server.biometrics.sensors.InvalidationRequesterClient; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.PerformanceTracker; import com.android.server.biometrics.sensors.face.FaceUtils; @@ -73,7 +75,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull private final String mHalInstanceName; @NonNull @VisibleForTesting final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports - @NonNull private final BaseClientMonitor.LazyDaemon<IFace> mLazyDaemon; + @NonNull private final HalClientMonitor.LazyDaemon<IFace> mLazyDaemon; @NonNull private final Handler mHandler; @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; @NonNull private final UsageStats mUsageStats; @@ -87,7 +89,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { public void onTaskStackChanged() { mHandler.post(() -> { for (int i = 0; i < mSensors.size(); i++) { - final BaseClientMonitor<?> client = mSensors.valueAt(i).getScheduler() + final BaseClientMonitor client = mSensors.valueAt(i).getScheduler() .getCurrentClient(); if (!(client instanceof AuthenticationClient)) { Slog.e(getTag(), "Task stack changed for client: " + client); @@ -181,7 +183,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { return mDaemon; } - private void scheduleForSensor(int sensorId, @NonNull BaseClientMonitor<?> client) { + private void scheduleForSensor(int sensorId, @NonNull BaseClientMonitor client) { if (!mSensors.contains(sensorId)) { throw new IllegalStateException("Unable to schedule client: " + client + " for sensor: " + sensorId); @@ -189,7 +191,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client); } - private void scheduleForSensor(int sensorId, @NonNull BaseClientMonitor<?> client, + private void scheduleForSensor(int sensorId, @NonNull BaseClientMonitor client, BaseClientMonitor.Callback callback) { if (!mSensors.contains(sensorId)) { throw new IllegalStateException("Unable to schedule client: " + client @@ -205,6 +207,12 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { // this method "withoutHandler" means it should only ever be invoked from the worker thread, // so callers will never be blocked. mSensors.get(sensorId).createNewSession(daemon, sensorId, userId); + + if (FaceUtils.getInstance(sensorId).isInvalidationInProgress(mContext, userId)) { + Slog.w(getTag(), "Scheduling unfinished invalidation request for sensor: " + sensorId + + ", user: " + userId); + scheduleInvalidationRequest(sensorId, userId); + } } @@ -241,6 +249,15 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { }); } + private void scheduleInvalidationRequest(int sensorId, int userId) { + mHandler.post(() -> { + final InvalidationRequesterClient<Face> client = + new InvalidationRequesterClient<>(mContext, userId, sensorId, + FaceUtils.getInstance(sensorId)); + mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client); + }); + } + @Override public boolean containsSensor(int sensorId) { return mSensors.contains(sensorId); @@ -390,10 +407,11 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser); scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() { @Override - public void onClientFinished(@NonNull BaseClientMonitor<?> clientMonitor, + public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { if (success) { scheduleLoadAuthenticatorIdsForUser(sensorId, userId); + scheduleInvalidationRequest(sensorId, userId); } } }); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java index 7edeb7b73a81..f355158d5727 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java @@ -26,7 +26,7 @@ import android.os.RemoteException; import android.util.Slog; import com.android.server.biometrics.HardwareAuthTokenUtils; -import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.HalClientMonitor; import com.android.server.biometrics.sensors.LockoutCache; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; @@ -36,7 +36,7 @@ import com.android.server.biometrics.sensors.LockoutTracker; * Updates the framework's lockout cache and notifies clients such as Keyguard when lockout is * cleared. */ -public class FaceResetLockoutClient extends BaseClientMonitor<ISession> { +public class FaceResetLockoutClient extends HalClientMonitor<ISession> { private static final String TAG = "FaceResetLockoutClient"; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java index 90df726ee03c..82ad387ab9f2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java @@ -48,6 +48,7 @@ import com.android.server.biometrics.sensors.AuthenticationConsumer; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.EnumerateConsumer; +import com.android.server.biometrics.sensors.HalClientMonitor; import com.android.server.biometrics.sensors.Interruptable; import com.android.server.biometrics.sensors.LockoutCache; import com.android.server.biometrics.sensors.LockoutConsumer; @@ -73,7 +74,7 @@ public class Sensor implements IBinder.DeathRecipient { @NonNull private final BiometricScheduler mScheduler; @NonNull private final LockoutCache mLockoutCache; @NonNull private final Map<Integer, Long> mAuthenticatorIds; - @NonNull private final BaseClientMonitor.LazyDaemon<ISession> mLazySession; + @NonNull private final HalClientMonitor.LazyDaemon<ISession> mLazySession; @Nullable private Session mCurrentSession; static class Session { @@ -136,7 +137,7 @@ public class Sensor implements IBinder.DeathRecipient { @Override public void onChallengeGenerated(long challenge) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof FaceGenerateChallengeClient)) { Slog.e(mTag, "onChallengeGenerated for wrong client: " + Utils.getClientName(client)); @@ -152,7 +153,7 @@ public class Sensor implements IBinder.DeathRecipient { @Override public void onChallengeRevoked(long challenge) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof FaceRevokeChallengeClient)) { Slog.e(mTag, "onChallengeRevoked for wrong client: " + Utils.getClientName(client)); @@ -168,7 +169,7 @@ public class Sensor implements IBinder.DeathRecipient { @Override public void onAcquired(byte info, int vendorCode) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof AcquisitionClient)) { Slog.e(mTag, "onAcquired for non-acquisition client: " + Utils.getClientName(client)); @@ -183,7 +184,7 @@ public class Sensor implements IBinder.DeathRecipient { @Override public void onError(byte error, int vendorCode) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); Slog.d(mTag, "onError" + ", client: " + Utils.getClientName(client) + ", error: " + error @@ -206,7 +207,7 @@ public class Sensor implements IBinder.DeathRecipient { @Override public void onEnrollmentProgress(int enrollmentId, int remaining) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof FaceEnrollClient)) { Slog.e(mTag, "onEnrollmentProgress for non-enroll client: " + Utils.getClientName(client)); @@ -226,7 +227,7 @@ public class Sensor implements IBinder.DeathRecipient { @Override public void onAuthenticationSucceeded(int enrollmentId, HardwareAuthToken hat) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof AuthenticationConsumer)) { Slog.e(mTag, "onAuthenticationSucceeded for non-authentication consumer: " + Utils.getClientName(client)); @@ -248,7 +249,7 @@ public class Sensor implements IBinder.DeathRecipient { @Override public void onAuthenticationFailed() { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof AuthenticationConsumer)) { Slog.e(mTag, "onAuthenticationFailed for non-authentication consumer: " + Utils.getClientName(client)); @@ -266,7 +267,7 @@ public class Sensor implements IBinder.DeathRecipient { @Override public void onLockoutTimed(long durationMillis) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof LockoutConsumer)) { Slog.e(mTag, "onLockoutTimed for non-lockout consumer: " + Utils.getClientName(client)); @@ -281,7 +282,7 @@ public class Sensor implements IBinder.DeathRecipient { @Override public void onLockoutPermanent() { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof LockoutConsumer)) { Slog.e(mTag, "onLockoutPermanent for non-lockout consumer: " + Utils.getClientName(client)); @@ -296,7 +297,7 @@ public class Sensor implements IBinder.DeathRecipient { @Override public void onLockoutCleared() { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof FaceResetLockoutClient)) { Slog.e(mTag, "onLockoutCleared for non-resetLockout client: " + Utils.getClientName(client)); @@ -316,7 +317,7 @@ public class Sensor implements IBinder.DeathRecipient { @Override public void onEnrollmentsEnumerated(int[] enrollmentIds) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof EnumerateConsumer)) { Slog.e(mTag, "onEnrollmentsEnumerated for non-enumerate consumer: " + Utils.getClientName(client)); @@ -339,7 +340,7 @@ public class Sensor implements IBinder.DeathRecipient { @Override public void onEnrollmentsRemoved(int[] enrollmentIds) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof RemovalConsumer)) { Slog.e(mTag, "onRemoved for non-removal consumer: " + Utils.getClientName(client)); @@ -361,7 +362,7 @@ public class Sensor implements IBinder.DeathRecipient { @Override public void onAuthenticatorIdRetrieved(long authenticatorId) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof FaceGetAuthenticatorIdClient)) { Slog.e(mTag, "onAuthenticatorIdRetrieved for wrong consumer: " + Utils.getClientName(client)); @@ -377,7 +378,7 @@ public class Sensor implements IBinder.DeathRecipient { @Override public void onAuthenticatorIdInvalidated(long newAuthenticatorId) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof FaceInvalidationClient)) { Slog.e(mTag, "onAuthenticatorIdInvalidated for wrong consumer: " + Utils.getClientName(client)); @@ -410,7 +411,7 @@ public class Sensor implements IBinder.DeathRecipient { }; } - @NonNull BaseClientMonitor.LazyDaemon<ISession> getLazySession() { + @NonNull HalClientMonitor.LazyDaemon<ISession> getLazySession() { return mLazySession; } @@ -491,7 +492,7 @@ public class Sensor implements IBinder.DeathRecipient { public void binderDied() { Slog.e(mTag, "Binder died"); mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (client instanceof Interruptable) { Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client); final Interruptable interruptable = (Interruptable) client; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java index fbc26c6498b3..50483d93f92b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java @@ -24,12 +24,13 @@ import android.hardware.common.NativeHandle; import android.hardware.keymaster.HardwareAuthToken; import android.os.Binder; import android.os.IBinder; +import android.util.Slog; /** * Test session that provides mostly no-ops. */ public class TestSession extends ISession.Stub { - private static final String TAG = "TestSession"; + private static final String TAG = "FaceTestSession"; @NonNull private final Sensor.HalSessionCallback mHalSessionCallback; @@ -86,12 +87,16 @@ public class TestSession extends ISession.Stub { @Override public void getAuthenticatorId(int cookie) { + Slog.d(TAG, "getAuthenticatorId"); + // Immediately return a value so the framework can continue with subsequent requests. mHalSessionCallback.onAuthenticatorIdRetrieved(0); } @Override public void invalidateAuthenticatorId(int cookie) { - + Slog.d(TAG, "invalidateAuthenticatorId"); + // Immediately return a value so the framework can continue with subsequent requests. + mHalSessionCallback.onAuthenticatorIdInvalidated(0); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index 85938360d4ae..5e7ddeb64fb4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -62,6 +62,7 @@ import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.EnumerateConsumer; +import com.android.server.biometrics.sensors.HalClientMonitor; import com.android.server.biometrics.sensors.Interruptable; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; @@ -103,7 +104,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @NonNull private final Context mContext; @NonNull private final BiometricScheduler mScheduler; @NonNull private final Handler mHandler; - @NonNull private final BaseClientMonitor.LazyDaemon<IBiometricsFace> mLazyDaemon; + @NonNull private final HalClientMonitor.LazyDaemon<IBiometricsFace> mLazyDaemon; @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; @NonNull private final LockoutHalImpl mLockoutTracker; @NonNull private final UsageStats mUsageStats; @@ -170,7 +171,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { .getUniqueName(mContext, userId); final Face face = new Face(name, faceId, deviceId); - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof FaceEnrollClient)) { Slog.e(TAG, "onEnrollResult for non-enroll client: " + Utils.getClientName(client)); @@ -186,7 +187,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { public void onAuthenticated(long deviceId, int faceId, int userId, ArrayList<Byte> token) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof AuthenticationConsumer)) { Slog.e(TAG, "onAuthenticated for non-authentication consumer: " + Utils.getClientName(client)); @@ -205,7 +206,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { public void onAcquired(long deviceId, int userId, int acquiredInfo, int vendorCode) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof AcquisitionClient)) { Slog.e(TAG, "onAcquired for non-acquire client: " + Utils.getClientName(client)); @@ -221,7 +222,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @Override public void onError(long deviceId, int userId, int error, int vendorCode) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); Slog.d(TAG, "handleError" + ", client: " + (client != null ? client.getOwnerString() : null) + ", error: " + error @@ -247,7 +248,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @Override public void onRemoved(long deviceId, ArrayList<Integer> removed, int userId) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof RemovalConsumer)) { Slog.e(TAG, "onRemoved for non-removal consumer: " + Utils.getClientName(client)); @@ -278,7 +279,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @Override public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof EnumerateConsumer)) { Slog.e(TAG, "onEnumerate for non-enumerate consumer: " + Utils.getClientName(client)); @@ -376,7 +377,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { mDaemon = null; mCurrentUserId = UserHandle.USER_NULL; - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (client instanceof Interruptable) { Slog.e(TAG, "Sending ERROR_HW_UNAVAILABLE for client: " + client); final Interruptable interruptable = (Interruptable) client; @@ -482,7 +483,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @Override public long getAuthenticatorId(int sensorId, int userId) { - return mAuthenticatorIds.get(userId); + return mAuthenticatorIds.getOrDefault(userId, 0L); } @Override @@ -530,7 +531,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { mSensorId, mCurrentChallengeOwner); mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() { @Override - public void onClientStarted(@NonNull BaseClientMonitor<?> clientMonitor) { + public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { if (client != clientMonitor) { Slog.e(TAG, "scheduleGenerateChallenge onClientStarted, mismatched client." + " Expecting: " + client + ", received: " + clientMonitor); @@ -559,7 +560,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { mLazyDaemon, token, opPackageName, mSensorId); mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() { @Override - public void onClientFinished(@NonNull BaseClientMonitor<?> clientMonitor, + public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { if (client != clientMonitor) { Slog.e(TAG, "scheduleRevokeChallenge, mismatched client." @@ -615,7 +616,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() { @Override - public void onClientFinished(@NonNull BaseClientMonitor<?> clientMonitor, + public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { if (success) { // Update authenticatorIds @@ -727,7 +728,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() { @Override public void onClientFinished( - @NonNull BaseClientMonitor<?> clientMonitor, boolean success) { + @NonNull BaseClientMonitor clientMonitor, boolean success) { if (success && feature == BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION) { final int settingsValue = client.getValue() ? 1 : 0; Slog.d(TAG, "Updating attention value for user: " + userId @@ -864,7 +865,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { hasEnrolled, mAuthenticatorIds); mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() { @Override - public void onClientFinished(@NonNull BaseClientMonitor<?> clientMonitor, + public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { if (success) { mCurrentUserId = targetUserId; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java index d30c9b102e6b..442303b037fb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java @@ -27,14 +27,14 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; -import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.HalClientMonitor; /** * Face-specific getFeature client supporting the {@link android.hardware.biometrics.face.V1_0} * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces. */ -public class FaceGetFeatureClient extends BaseClientMonitor<IBiometricsFace> { +public class FaceGetFeatureClient extends HalClientMonitor<IBiometricsFace> { private static final String TAG = "FaceGetFeatureClient"; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java index bb8386099109..e0548e073a98 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java @@ -23,7 +23,7 @@ import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.os.RemoteException; import android.util.Slog; -import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.HalClientMonitor; import java.util.ArrayList; @@ -31,7 +31,7 @@ import java.util.ArrayList; * Face-specific resetLockout client supporting the {@link android.hardware.biometrics.face.V1_0} * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces. */ -public class FaceResetLockoutClient extends BaseClientMonitor<IBiometricsFace> { +public class FaceResetLockoutClient extends HalClientMonitor<IBiometricsFace> { private static final String TAG = "FaceResetLockoutClient"; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java index 0fb0de2a65f3..43560434f147 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java @@ -25,8 +25,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; -import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.HalClientMonitor; import java.util.ArrayList; @@ -34,7 +34,7 @@ import java.util.ArrayList; * Face-specific setFeature client supporting the {@link android.hardware.biometrics.face.V1_0} * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces. */ -public class FaceSetFeatureClient extends BaseClientMonitor<IBiometricsFace> { +public class FaceSetFeatureClient extends HalClientMonitor<IBiometricsFace> { private static final String TAG = "FaceSetFeatureClient"; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java index 78ee71486b92..0e72f941b4d2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java @@ -24,12 +24,12 @@ import android.os.Environment; import android.os.RemoteException; import android.util.Slog; -import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.HalClientMonitor; import java.io.File; import java.util.Map; -public class FaceUpdateActiveUserClient extends BaseClientMonitor<IBiometricsFace> { +public class FaceUpdateActiveUserClient extends HalClientMonitor<IBiometricsFace> { private static final String TAG = "FaceUpdateActiveUserClient"; private static final String FACE_DATA_DIR = "facedata"; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index 82dc16133a0c..c413c8b6362e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -79,6 +79,13 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp } @Override + public void onError(int errorCode, int vendorCode) { + super.onError(errorCode, vendorCode); + + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + } + + @Override protected void startHalOperation() { UdfpsHelper.showUdfpsOverlay(getSensorId(), IUdfpsOverlayController.REASON_AUTH, mUdfpsOverlayController); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index cacc3661b1d8..0864c1a69a6f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -71,6 +71,13 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps { } @Override + public void onError(int errorCode, int vendorCode) { + super.onError(errorCode, vendorCode); + + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + } + + @Override protected boolean hasReachedEnrollmentLimit() { return FingerprintUtils.getInstance(getSensorId()) .getBiometricsForUser(getContext(), getTargetUserId()).size() diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java index 817004149389..02d4ac3dd98e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java @@ -23,11 +23,11 @@ import android.hardware.biometrics.fingerprint.ISession; import android.os.RemoteException; import android.util.Slog; -import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.HalClientMonitor; import java.util.Map; -class FingerprintGetAuthenticatorIdClient extends BaseClientMonitor<ISession> { +class FingerprintGetAuthenticatorIdClient extends HalClientMonitor<ISession> { private static final String TAG = "FingerprintGetAuthenticatorIdClient"; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index d65ecfffcf79..8a666f9acb7f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -46,6 +46,8 @@ import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.HalClientMonitor; +import com.android.server.biometrics.sensors.InvalidationRequesterClient; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.PerformanceTracker; import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; @@ -74,7 +76,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull private final String mHalInstanceName; @NonNull @VisibleForTesting final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports - @NonNull private final BaseClientMonitor.LazyDaemon<IFingerprint> mLazyDaemon; + @NonNull private final HalClientMonitor.LazyDaemon<IFingerprint> mLazyDaemon; @NonNull private final Handler mHandler; @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; @NonNull private final ActivityTaskManager mActivityTaskManager; @@ -88,7 +90,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi public void onTaskStackChanged() { mHandler.post(() -> { for (int i = 0; i < mSensors.size(); i++) { - final BaseClientMonitor<?> client = mSensors.valueAt(i).getScheduler() + final BaseClientMonitor client = mSensors.valueAt(i).getScheduler() .getCurrentClient(); if (!(client instanceof AuthenticationClient)) { Slog.e(getTag(), "Task stack changed for client: " + client); @@ -187,7 +189,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi return mDaemon; } - private void scheduleForSensor(int sensorId, @NonNull BaseClientMonitor<?> client) { + private void scheduleForSensor(int sensorId, @NonNull BaseClientMonitor client) { if (!mSensors.contains(sensorId)) { throw new IllegalStateException("Unable to schedule client: " + client + " for sensor: " + sensorId); @@ -195,7 +197,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client); } - private void scheduleForSensor(int sensorId, @NonNull BaseClientMonitor<?> client, + private void scheduleForSensor(int sensorId, @NonNull BaseClientMonitor client, BaseClientMonitor.Callback callback) { if (!mSensors.contains(sensorId)) { throw new IllegalStateException("Unable to schedule client: " + client @@ -211,6 +213,12 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi // this method "withoutHandler" means it should only ever be invoked from the worker thread, // so callers will never be blocked. mSensors.get(sensorId).createNewSession(daemon, sensorId, userId); + + if (FingerprintUtils.getInstance(sensorId).isInvalidationInProgress(mContext, userId)) { + Slog.w(getTag(), "Scheduling unfinished invalidation request for sensor: " + sensorId + + ", user: " + userId); + scheduleInvalidationRequest(sensorId, userId); + } } @Override @@ -267,6 +275,15 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi }); } + private void scheduleInvalidationRequest(int sensorId, int userId) { + mHandler.post(() -> { + final InvalidationRequesterClient<Fingerprint> client = + new InvalidationRequesterClient<>(mContext, userId, sensorId, + FingerprintUtils.getInstance(sensorId)); + mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client); + }); + } + @Override public void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) { mHandler.post(() -> { @@ -373,10 +390,11 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mUdfpsOverlayController, maxTemplatesPerUser, shouldLogMetrics); scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() { @Override - public void onClientFinished(@NonNull BaseClientMonitor<?> clientMonitor, + public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { if (success) { scheduleLoadAuthenticatorIdsForUser(sensorId, userId); + scheduleInvalidationRequest(sensorId, userId); } } }); @@ -581,7 +599,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public void onPointerDown(int sensorId, int x, int y, float minor, float major) { - final BaseClientMonitor<?> client = + final BaseClientMonitor client = mSensors.get(sensorId).getScheduler().getCurrentClient(); if (!(client instanceof Udfps)) { Slog.e(getTag(), "onPointerDown received during client: " + client); @@ -593,7 +611,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public void onPointerUp(int sensorId) { - final BaseClientMonitor<?> client = + final BaseClientMonitor client = mSensors.get(sensorId).getScheduler().getCurrentClient(); if (!(client instanceof Udfps)) { Slog.e(getTag(), "onPointerUp received during client: " + client); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java index 3bdcc1dc4a32..cd84cdfda5e5 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java @@ -26,7 +26,7 @@ import android.os.RemoteException; import android.util.Slog; import com.android.server.biometrics.HardwareAuthTokenUtils; -import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.HalClientMonitor; import com.android.server.biometrics.sensors.LockoutCache; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; @@ -36,7 +36,7 @@ import com.android.server.biometrics.sensors.LockoutTracker; * Updates the framework's lockout cache and notifies clients such as Keyguard when lockout is * cleared. */ -class FingerprintResetLockoutClient extends BaseClientMonitor<ISession> { +class FingerprintResetLockoutClient extends HalClientMonitor<ISession> { private static final String TAG = "FingerprintResetLockoutClient"; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java index c0f0577aac0e..911f6b48af41 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java @@ -48,6 +48,7 @@ import com.android.server.biometrics.sensors.AuthenticationConsumer; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.EnumerateConsumer; +import com.android.server.biometrics.sensors.HalClientMonitor; import com.android.server.biometrics.sensors.Interruptable; import com.android.server.biometrics.sensors.LockoutCache; import com.android.server.biometrics.sensors.LockoutConsumer; @@ -78,7 +79,7 @@ class Sensor implements IBinder.DeathRecipient { @NonNull private final Map<Integer, Long> mAuthenticatorIds; @Nullable private Session mCurrentSession; - @NonNull private final BaseClientMonitor.LazyDaemon<ISession> mLazySession; + @NonNull private final HalClientMonitor.LazyDaemon<ISession> mLazySession; static class Session { @NonNull private final String mTag; @@ -136,7 +137,7 @@ class Sensor implements IBinder.DeathRecipient { @Override public void onChallengeGenerated(long challenge) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof FingerprintGenerateChallengeClient)) { Slog.e(mTag, "onChallengeGenerated for wrong client: " + Utils.getClientName(client)); @@ -152,7 +153,7 @@ class Sensor implements IBinder.DeathRecipient { @Override public void onChallengeRevoked(long challenge) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof FingerprintRevokeChallengeClient)) { Slog.e(mTag, "onChallengeRevoked for wrong client: " + Utils.getClientName(client)); @@ -168,7 +169,7 @@ class Sensor implements IBinder.DeathRecipient { @Override public void onAcquired(byte info, int vendorCode) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof AcquisitionClient)) { Slog.e(mTag, "onAcquired for non-acquisition client: " + Utils.getClientName(client)); @@ -183,7 +184,7 @@ class Sensor implements IBinder.DeathRecipient { @Override public void onError(byte error, int vendorCode) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); Slog.d(mTag, "onError" + ", client: " + Utils.getClientName(client) + ", error: " + error @@ -206,7 +207,7 @@ class Sensor implements IBinder.DeathRecipient { @Override public void onEnrollmentProgress(int enrollmentId, int remaining) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof FingerprintEnrollClient)) { Slog.e(mTag, "onEnrollmentProgress for non-enroll client: " + Utils.getClientName(client)); @@ -226,7 +227,7 @@ class Sensor implements IBinder.DeathRecipient { @Override public void onAuthenticationSucceeded(int enrollmentId, HardwareAuthToken hat) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof AuthenticationConsumer)) { Slog.e(mTag, "onAuthenticationSucceeded for non-authentication consumer: " + Utils.getClientName(client)); @@ -249,7 +250,7 @@ class Sensor implements IBinder.DeathRecipient { @Override public void onAuthenticationFailed() { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof AuthenticationConsumer)) { Slog.e(mTag, "onAuthenticationFailed for non-authentication consumer: " + Utils.getClientName(client)); @@ -267,7 +268,7 @@ class Sensor implements IBinder.DeathRecipient { @Override public void onLockoutTimed(long durationMillis) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof LockoutConsumer)) { Slog.e(mTag, "onLockoutTimed for non-lockout consumer: " + Utils.getClientName(client)); @@ -282,7 +283,7 @@ class Sensor implements IBinder.DeathRecipient { @Override public void onLockoutPermanent() { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof LockoutConsumer)) { Slog.e(mTag, "onLockoutPermanent for non-lockout consumer: " + Utils.getClientName(client)); @@ -297,7 +298,7 @@ class Sensor implements IBinder.DeathRecipient { @Override public void onLockoutCleared() { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof FingerprintResetLockoutClient)) { Slog.e(mTag, "onLockoutCleared for non-resetLockout client: " + Utils.getClientName(client)); @@ -313,7 +314,7 @@ class Sensor implements IBinder.DeathRecipient { @Override public void onInteractionDetected() { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof FingerprintDetectClient)) { Slog.e(mTag, "onInteractionDetected for non-detect client: " + Utils.getClientName(client)); @@ -329,7 +330,7 @@ class Sensor implements IBinder.DeathRecipient { @Override public void onEnrollmentsEnumerated(int[] enrollmentIds) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof EnumerateConsumer)) { Slog.e(mTag, "onEnrollmentsEnumerated for non-enumerate consumer: " + Utils.getClientName(client)); @@ -352,7 +353,7 @@ class Sensor implements IBinder.DeathRecipient { @Override public void onEnrollmentsRemoved(int[] enrollmentIds) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof RemovalConsumer)) { Slog.e(mTag, "onRemoved for non-removal consumer: " + Utils.getClientName(client)); @@ -374,7 +375,7 @@ class Sensor implements IBinder.DeathRecipient { @Override public void onAuthenticatorIdRetrieved(long authenticatorId) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof FingerprintGetAuthenticatorIdClient)) { Slog.e(mTag, "onAuthenticatorIdRetrieved for wrong consumer: " + Utils.getClientName(client)); @@ -390,7 +391,7 @@ class Sensor implements IBinder.DeathRecipient { @Override public void onAuthenticatorIdInvalidated(long newAuthenticatorId) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof FingerprintInvalidationClient)) { Slog.e(mTag, "onAuthenticatorIdInvalidated for wrong consumer: " + Utils.getClientName(client)); @@ -424,7 +425,7 @@ class Sensor implements IBinder.DeathRecipient { }; } - @NonNull BaseClientMonitor.LazyDaemon<ISession> getLazySession() { + @NonNull HalClientMonitor.LazyDaemon<ISession> getLazySession() { return mLazySession; } @@ -505,7 +506,7 @@ class Sensor implements IBinder.DeathRecipient { public void binderDied() { Slog.e(mTag, "Binder died"); mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (client instanceof Interruptable) { Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client); final Interruptable interruptable = (Interruptable) client; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java index ddae1107ff77..ac4f6651613d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java @@ -30,7 +30,7 @@ import android.util.Slog; */ class TestSession extends ISession.Stub { - private static final String TAG = "TestSession"; + private static final String TAG = "FingerprintTestSession"; @NonNull private final Sensor.HalSessionCallback mHalSessionCallback; @@ -92,7 +92,9 @@ class TestSession extends ISession.Stub { @Override public void invalidateAuthenticatorId(int cookie) { - + Slog.d(TAG, "invalidateAuthenticatorId"); + // Immediately return a value so the framework can continue with subsequent requests. + mHalSessionCallback.onAuthenticatorIdInvalidated(0); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index e57e675b4455..6cc8687b3426 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -62,6 +62,7 @@ import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.EnumerateConsumer; +import com.android.server.biometrics.sensors.HalClientMonitor; import com.android.server.biometrics.sensors.Interruptable; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; @@ -103,7 +104,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider private final LockoutResetDispatcher mLockoutResetDispatcher; private final LockoutFrameworkImpl mLockoutTracker; private final BiometricTaskStackListener mTaskStackListener; - private final BaseClientMonitor.LazyDaemon<IBiometricsFingerprint> mLazyDaemon; + private final HalClientMonitor.LazyDaemon<IBiometricsFingerprint> mLazyDaemon; private final Map<Integer, Long> mAuthenticatorIds; @Nullable private IBiometricsFingerprint mDaemon; @@ -116,7 +117,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @Override public void onTaskStackChanged() { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof AuthenticationClient)) { Slog.e(TAG, "Task stack changed for client: " + client); return; @@ -188,7 +189,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @Override public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof FingerprintEnrollClient)) { Slog.e(TAG, "onEnrollResult for non-enroll client: " + Utils.getClientName(client)); @@ -213,7 +214,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @Override public void onAcquired_2_2(long deviceId, int acquiredInfo, int vendorCode) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof AcquisitionClient)) { Slog.e(TAG, "onAcquired for non-acquisition client: " + Utils.getClientName(client)); @@ -229,7 +230,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider public void onAuthenticated(long deviceId, int fingerId, int groupId, ArrayList<Byte> token) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof AuthenticationConsumer)) { Slog.e(TAG, "onAuthenticated for non-authentication consumer: " + Utils.getClientName(client)); @@ -247,7 +248,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @Override public void onError(long deviceId, int error, int vendorCode) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); Slog.d(TAG, "handleError" + ", client: " + Utils.getClientName(client) + ", error: " + error @@ -273,7 +274,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider public void onRemoved(long deviceId, int fingerId, int groupId, int remaining) { mHandler.post(() -> { Slog.d(TAG, "Removed, fingerId: " + fingerId + ", remaining: " + remaining); - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof RemovalConsumer)) { Slog.e(TAG, "onRemoved for non-removal consumer: " + Utils.getClientName(client)); @@ -289,7 +290,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @Override public void onEnumerate(long deviceId, int fingerId, int groupId, int remaining) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof EnumerateConsumer)) { Slog.e(TAG, "onEnumerate for non-enumerate consumer: " + Utils.getClientName(client)); @@ -379,7 +380,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mDaemon = null; mCurrentUserId = UserHandle.USER_NULL; - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (client instanceof Interruptable) { Slog.e(TAG, "Sending ERROR_HW_UNAVAILABLE for client: " + client); final Interruptable interruptable = (Interruptable) client; @@ -486,7 +487,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider hasEnrolled, mAuthenticatorIds); mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() { @Override - public void onClientFinished(@NonNull BaseClientMonitor<?> clientMonitor, + public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { if (success) { mCurrentUserId = targetUserId; @@ -560,7 +561,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider shouldLogMetrics); mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() { @Override - public void onClientFinished(@NonNull BaseClientMonitor<?> clientMonitor, + public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { if (success) { // Update authenticatorIds @@ -682,12 +683,12 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @Override public long getAuthenticatorId(int sensorId, int userId) { - return mAuthenticatorIds.get(userId); + return mAuthenticatorIds.getOrDefault(userId, 0L); } @Override public void onPointerDown(int sensorId, int x, int y, float minor, float major) { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof Udfps)) { Slog.w(TAG, "onFingerDown received during client: " + client); return; @@ -698,7 +699,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @Override public void onPointerUp(int sensorId) { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof Udfps)) { Slog.w(TAG, "onFingerDown received during client: " + client); return; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java index 3ea23666b764..2394a70b20bb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java @@ -146,14 +146,14 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage class TestableInternalCallback extends InternalCallback { @Override - public void onClientStarted(BaseClientMonitor<?> clientMonitor) { + public void onClientStarted(BaseClientMonitor clientMonitor) { super.onClientStarted(clientMonitor); Slog.d(TAG, "Client started: " + clientMonitor); mFingerprint21.setDebugMessage("Started: " + clientMonitor); } @Override - public void onClientFinished(BaseClientMonitor<?> clientMonitor, boolean success) { + public void onClientFinished(BaseClientMonitor clientMonitor, boolean success) { super.onClientFinished(clientMonitor, success); Slog.d(TAG, "Client finished: " + clientMonitor); mFingerprint21.setDebugMessage("Finished: " + clientMonitor); @@ -233,7 +233,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage public void onAuthenticated(long deviceId, int fingerId, int groupId, ArrayList<Byte> token) { mHandler.post(() -> { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof AuthenticationConsumer)) { Slog.e(TAG, "Non authentication consumer: " + client); return; @@ -360,7 +360,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage @Override public void run() { - final BaseClientMonitor<?> client = mScheduler.getCurrentClient(); + final BaseClientMonitor client = mScheduler.getCurrentClient(); // We don't care about FingerprintDetectClient, since accept/rejects are both OK. UDFPS // rejects will just simulate the path where non-enrolled fingers are presented. @@ -466,7 +466,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage Slog.d(TAG, "onFingerDown"); final AuthenticationConsumer lastAuthenticatedConsumer = mMockHalResultController.getLastAuthenticatedClient(); - final BaseClientMonitor<?> currentScheduledClient = mScheduler.getCurrentClient(); + final BaseClientMonitor currentScheduledClient = mScheduler.getCurrentClient(); if (currentScheduledClient == null) { Slog.d(TAG, "Not authenticating"); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java index 784e37bed553..13e2e4fd7a37 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java @@ -101,6 +101,13 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi } } + @Override + public void onError(int errorCode, int vendorCode) { + super.onError(errorCode, vendorCode); + + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + } + private void resetFailedAttempts(int userId) { mLockoutFrameworkImpl.resetFailedAttemptsForUser(true /* clearAttemptCounter */, userId); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java index b2e3c3302bbf..8493af13abd4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java @@ -113,6 +113,13 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint } @Override + public void onError(int errorCode, int vendorCode) { + super.onError(errorCode, vendorCode); + + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + } + + @Override public void onPointerDown(int x, int y, float minor, float major) { UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java index 2a4c2ef5b181..f6ec4d943543 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java @@ -26,7 +26,7 @@ import android.os.RemoteException; import android.os.SELinux; import android.util.Slog; -import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.HalClientMonitor; import java.io.File; import java.util.Map; @@ -34,7 +34,7 @@ import java.util.Map; /** * Sets the HAL's current active user, and updates the framework's authenticatorId cache. */ -public class FingerprintUpdateActiveUserClient extends BaseClientMonitor<IBiometricsFingerprint> { +public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometricsFingerprint> { private static final String TAG = "FingerprintUpdateActiveUserClient"; private static final String FP_DATA_DIR = "fpdata"; diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java index 1024556c17eb..26244e62696b 100644 --- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java +++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java @@ -368,6 +368,7 @@ final public class IpConnectivityMetrics extends SystemService { @Override public void logDefaultNetworkValidity(boolean valid) { + NetworkStack.checkNetworkStackPermission(getContext()); mDefaultNetworkMetrics.logDefaultNetworkValidity(SystemClock.elapsedRealtime(), valid); } @@ -375,6 +376,7 @@ final public class IpConnectivityMetrics extends SystemService { public void logDefaultNetworkEvent(Network defaultNetwork, int score, boolean validated, LinkProperties lp, NetworkCapabilities nc, Network previousDefaultNetwork, int previousScore, LinkProperties previousLp, NetworkCapabilities previousNc) { + NetworkStack.checkNetworkStackPermission(getContext()); final long timeMs = SystemClock.elapsedRealtime(); mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, defaultNetwork, score, validated, lp, nc, previousDefaultNetwork, previousScore, previousLp, previousNc); diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java index c1b1b6a2f26c..952193b77681 100644 --- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java +++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java @@ -246,11 +246,6 @@ public class Nat464Xlat extends BaseNetworkObserver { return; } - if (mNetwork.linkProperties == null) { - Log.e(TAG, "startClat: Can't start clat with null LinkProperties"); - return; - } - String baseIface = mNetwork.linkProperties.getInterfaceName(); if (baseIface == null) { Log.e(TAG, "startClat: Can't start clat on null interface"); diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index b0a73f105725..ba6cbcd3c796 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -136,12 +136,12 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // This Network object should always be used if possible, so as to encourage reuse of the // enclosed socket factory and connection pool. Avoid creating other Network objects. // This Network object is always valid. - public final Network network; - public LinkProperties linkProperties; + @NonNull public final Network network; + @NonNull public LinkProperties linkProperties; // This should only be modified by ConnectivityService, via setNetworkCapabilities(). // TODO: make this private with a getter. - public NetworkCapabilities networkCapabilities; - public final NetworkAgentConfig networkAgentConfig; + @NonNull public NetworkCapabilities networkCapabilities; + @NonNull public final NetworkAgentConfig networkAgentConfig; // Underlying networks declared by the agent. Only set if supportsUnderlyingNetworks is true. // The networks in this list might be declared by a VPN app using setUnderlyingNetworks and are @@ -329,6 +329,12 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd, IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber, int creatorUid) { + Objects.requireNonNull(net); + Objects.requireNonNull(info); + Objects.requireNonNull(lp); + Objects.requireNonNull(nc); + Objects.requireNonNull(context); + Objects.requireNonNull(config); networkAgent = na; network = net; networkInfo = info; @@ -536,19 +542,22 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { } @Override - public void sendNetworkCapabilities(NetworkCapabilities nc) { + public void sendNetworkCapabilities(@NonNull NetworkCapabilities nc) { + Objects.requireNonNull(nc); mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED, new Pair<>(NetworkAgentInfo.this, nc)).sendToTarget(); } @Override - public void sendLinkProperties(LinkProperties lp) { + public void sendLinkProperties(@NonNull LinkProperties lp) { + Objects.requireNonNull(lp); mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED, new Pair<>(NetworkAgentInfo.this, lp)).sendToTarget(); } @Override - public void sendNetworkInfo(NetworkInfo info) { + public void sendNetworkInfo(@NonNull NetworkInfo info) { + Objects.requireNonNull(info); mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_INFO_CHANGED, new Pair<>(NetworkAgentInfo.this, info)).sendToTarget(); } @@ -603,7 +612,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { * * @return the old capabilities of this network. */ - public synchronized NetworkCapabilities getAndSetNetworkCapabilities( + @NonNull public synchronized NetworkCapabilities getAndSetNetworkCapabilities( @NonNull final NetworkCapabilities nc) { final NetworkCapabilities oldNc = networkCapabilities; networkCapabilities = nc; diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index a65f8097121d..fb1e8197ccff 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -439,6 +439,11 @@ public class Vpn { mEnableTeardown = enableTeardown; } + @VisibleForTesting + public boolean getEnableTeardown() { + return mEnableTeardown; + } + /** * Update current state, dispatching event to listeners. */ @@ -2146,6 +2151,11 @@ public class Vpn { // Start a new LegacyVpnRunner and we are done! mVpnRunner = new LegacyVpnRunner(config, racoon, mtpd, profile); + startLegacyVpnRunner(); + } + + @VisibleForTesting + protected void startLegacyVpnRunner() { mVpnRunner.start(); } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 60e4595a7679..55103ca6cd1c 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -49,6 +49,7 @@ import android.database.ContentObserver; import android.graphics.ColorSpace; import android.graphics.Point; import android.hardware.SensorManager; +import android.hardware.devicestate.DeviceStateManager; import android.hardware.display.AmbientBrightnessDayStats; import android.hardware.display.BrightnessChangeEvent; import android.hardware.display.BrightnessConfiguration; @@ -71,6 +72,7 @@ import android.media.projection.IMediaProjectionManager; import android.net.Uri; import android.os.Binder; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.IBinder; import android.os.IBinder.DeathRecipient; import android.os.Looper; @@ -100,7 +102,6 @@ import android.util.Spline; import android.view.Display; import android.view.DisplayEventReceiver; import android.view.DisplayInfo; -import android.view.IDisplayFoldListener; import android.view.Surface; import android.view.SurfaceControl; @@ -114,7 +115,6 @@ import com.android.server.DisplayThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiThread; -import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.SurfaceAnimationThread; import com.android.server.wm.WindowManagerInternal; @@ -360,8 +360,8 @@ public final class DisplayManagerService extends SystemService { // Receives notifications about changes to Settings. private SettingsObserver mSettingsObserver; - // Received notifications of the display-fold action - private DisplayFoldListener mDisplayFoldListener; + // Received notifications of the device-state action (such as "fold", "unfold") + private DeviceStateManager mDeviceStateManager; private final boolean mAllowNonNativeRefreshRateOverride; @@ -504,10 +504,11 @@ public final class DisplayManagerService extends SystemService { synchronized (mSyncRoot) { mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); - WindowManagerPolicy policy = LocalServices.getService(WindowManagerPolicy.class); - mDisplayFoldListener = new DisplayFoldListener(); - policy.registerDisplayFoldListener(mDisplayFoldListener); + DeviceStateManager deviceStateManager = + mContext.getSystemService(DeviceStateManager.class); + deviceStateManager.registerDeviceStateListener(new DeviceStateListener(), + new HandlerExecutor(mHandler)); scheduleTraversalLocked(false); } @@ -2880,15 +2881,14 @@ public final class DisplayManagerService extends SystemService { } } - class DisplayFoldListener extends IDisplayFoldListener.Stub { + /** + * Listens to changes in device state and reports the state to LogicalDisplayMapper. + */ + class DeviceStateListener implements DeviceStateManager.DeviceStateListener { @Override - public void onDisplayFoldChanged(int displayId, boolean folded) { - // TODO: multi-display - IDisplayFoldListener callback only really works for the - // Display.DEFAULT_DISPLAY. - if (displayId == Display.DEFAULT_DISPLAY) { - synchronized (mSyncRoot) { - mLogicalDisplayMapper.setDeviceFoldedLocked(folded); - } + public void onDeviceStateChanged(int deviceState) { + synchronized (mSyncRoot) { + mLogicalDisplayMapper.setDeviceStateLocked(deviceState); } } }; diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index a12785889bd3..bb2fbed354aa 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -103,6 +103,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { private final DisplayDeviceRepository mDisplayDeviceRepo; private final Listener mListener; + private final int mFoldedDeviceState; LogicalDisplayMapper(Context context, DisplayDeviceRepository repo, Listener listener) { mDisplayDeviceRepo = repo; @@ -110,6 +111,9 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false); mDisplayDeviceRepo.addListener(this); + mFoldedDeviceState = context.getResources().getInteger( + com.android.internal.R.integer.config_foldedDeviceState); + loadFoldedDisplayConfig(context); } @@ -211,6 +215,10 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } } + void setDeviceStateLocked(int state) { + setDeviceFoldedLocked(state == mFoldedDeviceState); + } + void setDeviceFoldedLocked(boolean isFolded) { mIsFolded = isFolded; if (mIsFoldedOverride != null) { diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java index 521ce6973522..a0d9e8e0a6fb 100644 --- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java @@ -20,15 +20,26 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.graphics.Typeface; +import android.graphics.fonts.FontFamily; +import android.graphics.fonts.FontFileUtil; +import android.graphics.fonts.SystemFonts; import android.os.SharedMemory; import android.system.ErrnoException; +import android.text.FontConfig; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; import com.android.server.SystemService; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.NioUtils; +import java.nio.channels.FileChannel; +import java.util.Map; /** A service for managing system fonts. */ // TODO(b/173619554): Add API to update fonts. @@ -36,6 +47,10 @@ public final class FontManagerService { private static final String TAG = "FontManagerService"; + // TODO: make this a DeviceConfig flag. + private static final boolean ENABLE_FONT_UPDATES = false; + private static final String FONT_FILES_DIR = "/data/fonts/files"; + /** Class to manage FontManagerService's lifecycle. */ public static final class Lifecycle extends SystemService { private final FontManagerService mService; @@ -52,37 +67,132 @@ public final class FontManagerService { @Override @Nullable public SharedMemory getSerializedSystemFontMap() { - return mService.getSerializedSystemFontMap(); + if (!Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { + return null; + } + return mService.getCurrentFontSettings().getSerializedSystemFontMap(); } }); } } - @GuardedBy("this") - @Nullable - private SharedMemory mSerializedSystemFontMap = null; + private static class OtfFontFileParser implements UpdatableFontDir.FontFileParser { + @Override + public long getVersion(File file) throws IOException { + ByteBuffer buffer = mmap(file); + try { + return FontFileUtil.getRevision(buffer, 0); + } finally { + NioUtils.freeDirectBuffer(buffer); + } + } - @Nullable - private SharedMemory getSerializedSystemFontMap() { - if (!Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { - return null; + private static ByteBuffer mmap(File file) throws IOException { + try (FileInputStream in = new FileInputStream(file)) { + FileChannel fileChannel = in.getChannel(); + return fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()); + } } + } + + @Nullable + private final UpdatableFontDir mUpdatableFontDir; + + @GuardedBy("FontManagerService.this") + @Nullable SystemFontSettings mCurrentFontSettings = null; + + private FontManagerService() { + mUpdatableFontDir = ENABLE_FONT_UPDATES + ? new UpdatableFontDir(new File(FONT_FILES_DIR), new OtfFontFileParser()) : null; + } + + @NonNull private SystemFontSettings getCurrentFontSettings() { synchronized (FontManagerService.this) { - if (mSerializedSystemFontMap == null) { - mSerializedSystemFontMap = createSerializedSystemFontMapLocked(); + if (mCurrentFontSettings == null) { + mCurrentFontSettings = SystemFontSettings.create(mUpdatableFontDir); } - return mSerializedSystemFontMap; + return mCurrentFontSettings; } } - @Nullable - private SharedMemory createSerializedSystemFontMapLocked() { - // TODO(b/173619554): use updated fonts. - try { - return Typeface.serializeFontMap(Typeface.getSystemFontMap()); - } catch (IOException | ErrnoException e) { - Slog.e(TAG, "Failed to serialize SystemServer system font map", e); + private boolean installFontFile(String name, FileDescriptor fd) { + if (mUpdatableFontDir == null) return false; + synchronized (FontManagerService.this) { + try { + mUpdatableFontDir.installFontFile(name, fd); + } catch (IOException e) { + Slog.w(TAG, "Failed to install font file: " + name, e); + return false; + } + // Create updated font map in the next getSerializedSystemFontMap() call. + mCurrentFontSettings = null; + return true; } - return null; } + + private static class SystemFontSettings { + private final @NonNull SharedMemory mSerializedSystemFontMap; + private final @NonNull FontConfig mSystemFontConfig; + private final @NonNull Map<String, FontFamily[]> mSystemFallbackMap; + private final @NonNull Map<String, Typeface> mSystemTypefaceMap; + + SystemFontSettings( + @NonNull SharedMemory serializedSystemFontMap, + @NonNull FontConfig systemFontConfig, + @NonNull Map<String, FontFamily[]> systemFallbackMap, + @NonNull Map<String, Typeface> systemTypefaceMap) { + mSerializedSystemFontMap = serializedSystemFontMap; + mSystemFontConfig = systemFontConfig; + mSystemFallbackMap = systemFallbackMap; + mSystemTypefaceMap = systemTypefaceMap; + } + + public @NonNull SharedMemory getSerializedSystemFontMap() { + return mSerializedSystemFontMap; + } + + public @NonNull FontConfig getSystemFontConfig() { + return mSystemFontConfig; + } + + public @NonNull Map<String, FontFamily[]> getSystemFallbackMap() { + return mSystemFallbackMap; + } + + public @NonNull Map<String, Typeface> getSystemTypefaceMap() { + return mSystemTypefaceMap; + } + + public static @Nullable SystemFontSettings create( + @Nullable UpdatableFontDir updatableFontDir) { + if (updatableFontDir != null) { + final FontConfig fontConfig = SystemFonts.getSystemFontConfig( + updatableFontDir.getFontFileMap()); + final Map<String, FontFamily[]> fallback = + SystemFonts.buildSystemFallback(fontConfig); + final Map<String, Typeface> typefaceMap = + SystemFonts.buildSystemTypefaces(fontConfig, fallback); + + try { + final SharedMemory shm = Typeface.serializeFontMap(typefaceMap); + return new SystemFontSettings(shm, fontConfig, fallback, typefaceMap); + } catch (IOException | ErrnoException e) { + Slog.w(TAG, "Failed to serialize updatable font map. " + + "Retrying with system image fonts.", e); + } + } + + final FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig(); + final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig); + final Map<String, Typeface> typefaceMap = + SystemFonts.buildSystemTypefaces(fontConfig, fallback); + try { + final SharedMemory shm = Typeface.serializeFontMap(typefaceMap); + return new SystemFontSettings(shm, fontConfig, fallback, typefaceMap); + } catch (IOException | ErrnoException e) { + Slog.e(TAG, "Failed to serialize SystemServer system font map", e); + } + return null; + } + }; } diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java new file mode 100644 index 000000000000..7306471e68c2 --- /dev/null +++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.graphics.fonts; + +import android.os.FileUtils; +import android.util.Base64; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; +import java.security.SecureRandom; +import java.util.HashMap; +import java.util.Map; + +final class UpdatableFontDir { + + private static final String TAG = "UpdatableFontDir"; + private static final String RANDOM_DIR_PREFIX = "~~"; + + /** Interface to mock font file access in tests. */ + interface FontFileParser { + long getVersion(File file) throws IOException; + } + + /** Data class to hold font file path and version. */ + static final class FontFileInfo { + final File mFile; + final long mVersion; + + FontFileInfo(File file, long version) { + mFile = file; + mVersion = version; + } + } + + /** + * Root directory for storing updated font files. Each font file is stored in a unique random + * dir. The font file path would be {@code mFilesDir/~~{randomStr}/{fontFileName}}. + */ + private final File mFilesDir; + private final FontFileParser mParser; + @GuardedBy("UpdatableFontDir.this") + private final Map<String, FontFileInfo> mFontFileInfoMap = new HashMap<>(); + + UpdatableFontDir(File filesDir, FontFileParser parser) { + mFilesDir = filesDir; + mParser = parser; + loadFontFileMap(); + } + + private void loadFontFileMap() { + synchronized (UpdatableFontDir.this) { + mFontFileInfoMap.clear(); + File[] dirs = mFilesDir.listFiles(); + if (dirs == null) return; + for (File dir : dirs) { + if (!dir.getName().startsWith(RANDOM_DIR_PREFIX)) continue; + File[] files = dir.listFiles(); + if (files == null || files.length != 1) continue; + addFileToMapLocked(files[0], true); + } + } + } + + void installFontFile(String name, FileDescriptor fd) throws IOException { + // TODO: Validate name. + synchronized (UpdatableFontDir.this) { + // TODO: proper error handling + File newDir = getRandomDir(mFilesDir); + if (!newDir.mkdir()) { + throw new IOException("Failed to create a new dir"); + } + File newFontFile = new File(newDir, name); + try (FileOutputStream out = new FileOutputStream(newFontFile)) { + FileUtils.copy(fd, out.getFD()); + } + addFileToMapLocked(newFontFile, false); + } + } + + /** + * Given {@code parent}, returns {@code parent/~~[randomStr]}. + * Makes sure that {@code parent/~~[randomStr]} directory doesn't exist. + * Notice that this method doesn't actually create any directory. + */ + private static File getRandomDir(File parent) { + SecureRandom random = new SecureRandom(); + byte[] bytes = new byte[16]; + File dir; + do { + random.nextBytes(bytes); + String dirName = RANDOM_DIR_PREFIX + + Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP); + dir = new File(parent, dirName); + } while (dir.exists()); + return dir; + } + + private void addFileToMapLocked(File file, boolean deleteOldFile) { + final long version; + try { + version = mParser.getVersion(file); + } catch (IOException e) { + Slog.e(TAG, "Failed to read font file", e); + return; + } + if (version == -1) { + Slog.e(TAG, "Invalid font file"); + return; + } + FontFileInfo info = mFontFileInfoMap.get(file.getName()); + if (info == null) { + // TODO: check version of font in /system/fonts and /product/fonts + mFontFileInfoMap.put(file.getName(), new FontFileInfo(file, version)); + } else if (info.mVersion < version) { + if (deleteOldFile) { + FileUtils.deleteContentsAndDir(info.mFile.getParentFile()); + } + mFontFileInfoMap.put(file.getName(), new FontFileInfo(file, version)); + } + } + + Map<String, File> getFontFileMap() { + Map<String, File> map = new HashMap<>(); + synchronized (UpdatableFontDir.this) { + for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) { + map.put(entry.getKey(), entry.getValue().mFile); + } + } + return map; + } +} diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index a207a96f4663..23c70ee69514 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -1856,6 +1856,13 @@ public class InputManagerService extends IInputManager.Stub } VibrationInfo(VibrationEffect effect) { + // First replace prebaked effects with its fallback, if any available. + if (effect instanceof VibrationEffect.Prebaked) { + VibrationEffect fallback = ((VibrationEffect.Prebaked) effect).getFallbackEffect(); + if (fallback != null) { + effect = fallback; + } + } if (effect instanceof VibrationEffect.OneShot) { VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect; mPattern = new long[] { 0, oneShot.getDuration() }; @@ -1882,8 +1889,7 @@ public class InputManagerService extends IInputManager.Stub throw new ArrayIndexOutOfBoundsException(); } } else { - // TODO: Add support for prebaked effects - Slog.w(TAG, "Pre-baked effects aren't supported on input devices"); + Slog.w(TAG, "Pre-baked and composed effects aren't supported on input devices"); } } } @@ -2059,8 +2065,7 @@ public class InputManagerService extends IInputManager.Stub @Override // Binder call public InputSensorInfo[] getSensorList(int deviceId) { - InputSensorInfo[] sensors = nativeGetSensorList(mPtr, deviceId); - return sensors; + return nativeGetSensorList(mPtr, deviceId); } @Override // Binder call diff --git a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java index b57681073e51..5fa799839789 100644 --- a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java +++ b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java @@ -21,6 +21,9 @@ import android.media.metrics.IPlaybackMetricsManager; import android.media.metrics.NetworkEvent; import android.media.metrics.PlaybackErrorEvent; import android.media.metrics.PlaybackMetrics; +import android.media.metrics.PlaybackStateEvent; +import android.media.metrics.TrackChangeEvent; +import android.os.Binder; import android.util.Base64; import android.util.StatsEvent; import android.util.StatsLog; @@ -54,6 +57,33 @@ public final class PlaybackMetricsManagerService extends SystemService { private final class BinderService extends IPlaybackMetricsManager.Stub { @Override public void reportPlaybackMetrics(String sessionId, PlaybackMetrics metrics, int userId) { + StatsEvent statsEvent = StatsEvent.newBuilder() + .setAtomId(320) + .writeInt(Binder.getCallingUid()) + .writeString(sessionId) + .writeLong(metrics.getMediaDurationMillis()) + .writeInt(metrics.getStreamSource()) + .writeInt(metrics.getStreamType()) + .writeInt(metrics.getPlaybackType()) + .writeInt(metrics.getDrmType()) + .writeInt(metrics.getContentType()) + .writeString(metrics.getPlayerName()) + .writeString(metrics.getPlayerVersion()) + .writeByteArray(new byte[0]) // TODO: write experiments proto + .writeInt(metrics.getVideoFramesPlayed()) + .writeInt(metrics.getVideoFramesDropped()) + .writeInt(metrics.getAudioUnderrunCount()) + .writeLong(metrics.getNetworkBytesRead()) + .writeLong(metrics.getLocalBytesRead()) + .writeLong(metrics.getNetworkTransferDurationMillis()) + .usePooledBuffer() + .build(); + StatsLog.write(statsEvent); + } + + @Override + public void reportPlaybackStateEvent( + String sessionId, PlaybackStateEvent event, int userId) { // TODO: log it to statsd } @@ -91,5 +121,30 @@ public final class PlaybackMetricsManagerService extends SystemService { .build(); StatsLog.write(statsEvent); } + + @Override + public void reportTrackChangeEvent( + String sessionId, TrackChangeEvent event, int userId) { + StatsEvent statsEvent = StatsEvent.newBuilder() + .setAtomId(321) + .writeString(sessionId) + .writeInt(event.getTrackState()) + .writeInt(event.getTrackChangeReason()) + .writeString(event.getContainerMimeType()) + .writeString(event.getSampleMimeType()) + .writeString(event.getCodecName()) + .writeInt(event.getBitrate()) + .writeLong(event.getTimeSincePlaybackCreatedMillis()) + .writeInt(event.getTrackType()) + .writeString(event.getLanguage()) + .writeString(event.getLanguageRegion()) + .writeInt(event.getChannelCount()) + .writeInt(event.getSampleRate()) + .writeInt(event.getWidth()) + .writeInt(event.getHeight()) + .usePooledBuffer() + .build(); + StatsLog.write(statsEvent); + } } } diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java index ea1d8da74185..ea2788c0c3d8 100644 --- a/services/core/java/com/android/server/net/LockdownVpnTracker.java +++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java @@ -34,7 +34,6 @@ import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkInfo.State; import android.os.Handler; -import android.security.Credentials; import android.security.KeyStore; import android.text.TextUtils; import android.util.Log; @@ -70,6 +69,7 @@ public class LockdownVpnTracker { @NonNull private final Handler mHandler; @NonNull private final Vpn mVpn; @NonNull private final VpnProfile mProfile; + @NonNull private final KeyStore mKeyStore; @NonNull private final Object mStateLock = new Object(); @@ -81,13 +81,10 @@ public class LockdownVpnTracker { private int mErrorCount; - public static boolean isEnabled() { - return KeyStore.getInstance().contains(Credentials.LOCKDOWN_VPN); - } - public LockdownVpnTracker(@NonNull Context context, @NonNull ConnectivityService connService, @NonNull Handler handler, + @NonNull KeyStore keyStore, @NonNull Vpn vpn, @NonNull VpnProfile profile) { mContext = Objects.requireNonNull(context); @@ -95,6 +92,7 @@ public class LockdownVpnTracker { mHandler = Objects.requireNonNull(handler); mVpn = Objects.requireNonNull(vpn); mProfile = Objects.requireNonNull(profile); + mKeyStore = Objects.requireNonNull(keyStore); mNotificationManager = mContext.getSystemService(NotificationManager.class); final Intent configIntent = new Intent(ACTION_VPN_SETTINGS); @@ -157,7 +155,7 @@ public class LockdownVpnTracker { try { // Use the privileged method because Lockdown VPN is initiated by the system, so // no additional permission checks are necessary. - mVpn.startLegacyVpnPrivileged(mProfile, KeyStore.getInstance(), egressProp); + mVpn.startLegacyVpnPrivileged(mProfile, mKeyStore, egressProp); } catch (IllegalStateException e) { mAcceptedEgressIface = null; Log.e(TAG, "Failed to start VPN", e); diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index cbd973aeaf6a..61c8b178b4c0 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -50,6 +50,7 @@ import android.service.notification.ConversationChannelWrapper; import android.service.notification.NotificationListenerService; import android.service.notification.RankingHelperProto; import android.text.TextUtils; +import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IntArray; @@ -101,6 +102,7 @@ public class PreferencesHelper implements RankingConfig { private static final int NOTIFICATION_PREFERENCES_PULL_LIMIT = 1000; private static final int NOTIFICATION_CHANNEL_PULL_LIMIT = 2000; private static final int NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT = 1000; + private static final int NOTIFICATION_CHANNEL_DELETION_RETENTION_DAYS = 30; @VisibleForTesting static final String TAG_RANKING = "ranking"; @@ -324,12 +326,8 @@ public class PreferencesHelper implements RankingConfig { channel.setImportanceLockedByOEM(true); } } - boolean isInvalidShortcutChannel = - channel.getConversationId() != null && - channel.getConversationId().contains( - PLACEHOLDER_CONVERSATION_ID); - if (mAllowInvalidShortcuts || (!mAllowInvalidShortcuts - && !isInvalidShortcutChannel)) { + + if (isShortcutOk(channel) && isDeletionOk(channel)) { r.channels.put(id, channel); } } @@ -369,6 +367,26 @@ public class PreferencesHelper implements RankingConfig { throw new IllegalStateException("Failed to reach END_DOCUMENT"); } + private boolean isShortcutOk(NotificationChannel channel) { + boolean isInvalidShortcutChannel = + channel.getConversationId() != null && + channel.getConversationId().contains( + PLACEHOLDER_CONVERSATION_ID); + return mAllowInvalidShortcuts || (!mAllowInvalidShortcuts && !isInvalidShortcutChannel); + } + + private boolean isDeletionOk(NotificationChannel nc) { + if (!nc.isDeleted()) { + return true; + } + long boundary = System.currentTimeMillis() - ( + DateUtils.DAY_IN_MILLIS * NOTIFICATION_CHANNEL_DELETION_RETENTION_DAYS); + if (nc.getDeletedTimeMs() <= boundary) { + return false; + } + return true; + } + private PackagePreferences getPackagePreferencesLocked(String pkg, int uid) { final String key = packagePreferencesKey(pkg, uid); return mPackagePreferences.get(key); @@ -828,6 +846,7 @@ public class PreferencesHelper implements RankingConfig { if (existing.isDeleted()) { // The existing channel was deleted - undelete it. existing.setDeleted(false); + existing.setDeletedTimeMs(-1); needsPolicyFileChange = true; wasUndeleted = true; @@ -1119,6 +1138,7 @@ public class PreferencesHelper implements RankingConfig { private void deleteNotificationChannelLocked(NotificationChannel channel, String pkg, int uid) { if (!channel.isDeleted()) { channel.setDeleted(true); + channel.setDeletedTimeMs(System.currentTimeMillis()); LogMaker lm = getChannelLog(channel, pkg); lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE); MetricsLogger.action(lm); @@ -1479,6 +1499,7 @@ public class PreferencesHelper implements RankingConfig { if (nc.getConversationId() != null && conversationIds.contains(nc.getConversationId())) { nc.setDeleted(true); + nc.setDeletedTimeMs(System.currentTimeMillis()); LogMaker lm = getChannelLog(nc, pkg); lm.setType( com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE); diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java index 01eeb31dff2b..ef0f0eeb56f8 100644 --- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java +++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java @@ -21,6 +21,7 @@ import android.annotation.RequiresPermission; import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; +import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.os.Binder; import android.os.BugreportParams; @@ -31,6 +32,7 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserManager; +import android.telephony.TelephonyManager; import android.util.ArraySet; import android.util.Slog; @@ -53,11 +55,13 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { private final Object mLock = new Object(); private final Context mContext; private final AppOpsManager mAppOps; + private final TelephonyManager mTelephonyManager; private final ArraySet<String> mBugreportWhitelistedPackages; BugreportManagerServiceImpl(Context context) { mContext = context; - mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + mAppOps = context.getSystemService(AppOpsManager.class); + mTelephonyManager = context.getSystemService(TelephonyManager.class); mBugreportWhitelistedPackages = SystemConfig.getInstance().getBugreportWhitelistedPackages(); } @@ -67,11 +71,14 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { public void startBugreport(int callingUidUnused, String callingPackage, FileDescriptor bugreportFd, FileDescriptor screenshotFd, int bugreportMode, IDumpstateListener listener, boolean isScreenshotRequested) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "startBugreport"); Objects.requireNonNull(callingPackage); Objects.requireNonNull(bugreportFd); Objects.requireNonNull(listener); validateBugreportMode(bugreportMode); + + int callingUid = Binder.getCallingUid(); + enforcePermission(callingPackage, callingUid, bugreportMode + == BugreportParams.BUGREPORT_MODE_TELEPHONY /* checkCarrierPrivileges */); final long identity = Binder.clearCallingIdentity(); try { ensureIsPrimaryUser(); @@ -79,13 +86,6 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { Binder.restoreCallingIdentity(identity); } - int callingUid = Binder.getCallingUid(); - mAppOps.checkPackage(callingUid, callingPackage); - - if (!mBugreportWhitelistedPackages.contains(callingPackage)) { - throw new SecurityException( - callingPackage + " is not whitelisted to use Bugreport API"); - } synchronized (mLock) { startBugreportLocked(callingUid, callingPackage, bugreportFd, screenshotFd, bugreportMode, listener, isScreenshotRequested); @@ -93,12 +93,10 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { } @Override - @RequiresPermission(android.Manifest.permission.DUMP) + @RequiresPermission(android.Manifest.permission.DUMP) // or carrier privileges public void cancelBugreport(int callingUidUnused, String callingPackage) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, - "cancelBugreport"); int callingUid = Binder.getCallingUid(); - mAppOps.checkPackage(callingUid, callingPackage); + enforcePermission(callingPackage, callingUid, true /* checkCarrierPrivileges */); synchronized (mLock) { IDumpstate ds = getDumpstateBinderServiceLocked(); @@ -134,6 +132,34 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { } } + private void enforcePermission( + String callingPackage, int callingUid, boolean checkCarrierPrivileges) { + mAppOps.checkPackage(callingUid, callingPackage); + + // To gain access through the DUMP permission, the OEM has to allow this package explicitly + // via sysconfig and privileged permissions. + if (mBugreportWhitelistedPackages.contains(callingPackage) + && mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + == PackageManager.PERMISSION_GRANTED) { + return; + } + // For carrier privileges, this can include user-installed apps. This is essentially a + // function of the current active SIM(s) in the device to let carrier apps through. + if (checkCarrierPrivileges + && mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(callingPackage) + == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { + return; + } + + String message = + callingPackage + + " does not hold the DUMP permission or is not bugreport-whitelisted " + + (checkCarrierPrivileges ? "and does not have carrier privileges " : "") + + "to request a bugreport"; + Slog.w(TAG, message); + throw new SecurityException(message); + } + /** * Validates that the current user is the primary user. * diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index b65fc7358471..2b5c393e7159 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -71,6 +71,7 @@ import android.content.pm.IDataLoaderStatusListener; import android.content.pm.IPackageInstallObserver2; import android.content.pm.IPackageInstallerSession; import android.content.pm.IPackageInstallerSessionFileSystemConnector; +import android.content.pm.IPackageLoadingProgressCallback; import android.content.pm.InstallationFile; import android.content.pm.InstallationFileParcel; import android.content.pm.PackageInfo; @@ -186,6 +187,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { static final String TAG_CHILD_SESSION = "childSession"; static final String TAG_SESSION_FILE = "sessionFile"; static final String TAG_SESSION_CHECKSUM = "sessionChecksum"; + static final String TAG_SESSION_CHECKSUM_SIGNATURE = "sessionChecksumSignature"; private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission"; private static final String TAG_WHITELISTED_RESTRICTED_PERMISSION = "whitelisted-restricted-permission"; @@ -319,6 +321,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private float mProgress = 0; @GuardedBy("mLock") private float mReportedProgress = -1; + @GuardedBy("mLock") + private float mIncrementalProgress = 0; /** State of the session. */ @GuardedBy("mLock") @@ -397,8 +401,26 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private ArraySet<FileEntry> mFiles = new ArraySet<>(); + static class PerFileChecksum { + private final Checksum[] mChecksums; + private final byte[] mSignature; + + PerFileChecksum(Checksum[] checksums, byte[] signature) { + mChecksums = checksums; + mSignature = signature; + } + + Checksum[] getChecksums() { + return this.mChecksums; + } + + byte[] getSignature() { + return this.mSignature; + } + } + @GuardedBy("mLock") - private ArrayMap<String, Checksum[]> mChecksums = new ArrayMap<>(); + private ArrayMap<String, PerFileChecksum> mChecksums = new ArrayMap<>(); @Nullable final StagedSession mStagedSession; @@ -921,7 +943,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { int sessionId, int userId, int installerUid, @NonNull InstallSource installSource, SessionParams params, long createdMillis, long committedMillis, File stageDir, String stageCid, InstallationFile[] files, - ArrayMap<String, List<Checksum>> checksums, + ArrayMap<String, PerFileChecksum> checksums, boolean prepared, boolean committed, boolean destroyed, boolean sealed, @Nullable int[] childSessionIds, int parentSessionId, boolean isReady, boolean isFailed, boolean isApplied, int stagedSessionErrorCode, @@ -967,11 +989,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } if (checksums != null) { - for (int i = 0, isize = checksums.size(); i < isize; ++i) { - final String fileName = checksums.keyAt(i); - final List<Checksum> fileChecksums = checksums.valueAt(i); - mChecksums.put(fileName, fileChecksums.toArray(new Checksum[fileChecksums.size()])); - } + mChecksums.putAll(checksums); } if (!params.isMultiPackage && (stageDir == null) == (stageCid == null)) { @@ -1182,7 +1200,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private void computeProgressLocked(boolean forcePublish) { - mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f) + // This method is triggered when the client progress is updated or the incremental progress + // is updated. For incremental installs, ignore the progress values reported from client. + // Instead, only use the progress reported by IncFs as the percentage of loading completion. + final float loadingProgress = + isIncrementalInstallation() ? mIncrementalProgress : mClientProgress; + mProgress = MathUtils.constrain(loadingProgress * 0.8f, 0f, 0.8f) + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f); // Only publish when meaningful change @@ -1253,7 +1276,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } @Override - public void addChecksums(String name, @NonNull Checksum[] checksums) { + public void setChecksums(String name, @NonNull Checksum[] checksums, + @Nullable byte[] signature) { if (checksums.length == 0) { return; } @@ -1269,6 +1293,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new IllegalStateException("Can't obtain calling installer's package."); } + if (signature != null && signature.length != 0) { + final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled(); + final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled(); + if (!standardMode || legacyMode) { + Slog.e(TAG, + "Can't enforce checksum's signature: Apk-Verity is disabled or in legacy " + + "mode."); + signature = null; + } + } + synchronized (mLock) { assertCallerIsOwnerOrRootLocked(); assertPreparedAndNotCommittedOrDestroyedLocked("addChecksums"); @@ -1277,7 +1312,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new IllegalStateException("Duplicate checksums."); } - mChecksums.put(name, checksums); + mChecksums.put(name, new PerFileChecksum(checksums, signature)); } } @@ -3032,15 +3067,25 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { maybeStageFsveritySignatureLocked(dexMetadataFile, targetDexMetadataFile); } + private void storeBytesToInstallationFile(final String localPath, final String absolutePath, + final byte[] bytes) throws IOException { + if (!isIncrementalInstallation() || mIncrementalFileStorages == null) { + FileUtils.bytesToFile(absolutePath, bytes); + } else { + mIncrementalFileStorages.makeFile(localPath, bytes); + } + } + @GuardedBy("mLock") private void maybeStageDigestsLocked(File origFile, File targetFile, String splitName) throws PackageManagerException { - final Checksum[] checksums = mChecksums.get(origFile.getName()); - if (checksums == null) { + final PerFileChecksum perFileChecksum = mChecksums.get(origFile.getName()); + if (perFileChecksum == null) { return; } mChecksums.remove(origFile.getName()); + final Checksum[] checksums = perFileChecksum.getChecksums(); if (checksums.length == 0) { return; } @@ -3048,14 +3093,24 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final String targetDigestsPath = ApkChecksums.buildDigestsPathForApk(targetFile.getName()); final File targetDigestsFile = new File(stageDir, targetDigestsPath); try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + // Storing and staging checksums. ApkChecksums.writeChecksums(os, checksums); - final byte[] checksumsBytes = os.toByteArray(); + storeBytesToInstallationFile(targetDigestsPath, targetDigestsFile.getAbsolutePath(), + os.toByteArray()); + stageFileLocked(targetDigestsFile, targetDigestsFile); - if (!isIncrementalInstallation() || mIncrementalFileStorages == null) { - FileUtils.bytesToFile(targetDigestsFile.getAbsolutePath(), checksumsBytes); - } else { - mIncrementalFileStorages.makeFile(targetDigestsPath, checksumsBytes); + final byte[] signature = perFileChecksum.getSignature(); + if (signature == null || signature.length == 0) { + return; } + + // Storing and staging signature. + final String targetDigestsSignaturePath = VerityUtils.getFsveritySignatureFilePath( + targetDigestsPath); + final File targetDigestsSignatureFile = new File(stageDir, targetDigestsSignaturePath); + storeBytesToInstallationFile(targetDigestsSignaturePath, + targetDigestsSignatureFile.getAbsolutePath(), signature); + stageFileLocked(targetDigestsSignatureFile, targetDigestsSignatureFile); } catch (CertificateException e) { throw new PackageManagerException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING, "Failed to encode certificate for " + mPackageName, e); @@ -3063,8 +3118,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed to store digests for " + mPackageName, e); } - - stageFileLocked(targetDigestsFile, targetDigestsFile); } @GuardedBy("mLock") @@ -3704,7 +3757,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { try { mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir, params, statusListener, healthCheckParams, healthListener, addedFiles, - perUidReadTimeouts); + perUidReadTimeouts, + new IPackageLoadingProgressCallback.Stub() { + @Override + public void onPackageLoadingProgressChanged(float progress) { + synchronized (mLock) { + mIncrementalProgress = progress; + computeProgressLocked(true); + } + } + }); return false; } catch (IOException e) { throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(), @@ -4277,7 +4339,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { for (int i = 0, isize = mChecksums.size(); i < isize; ++i) { final String fileName = mChecksums.keyAt(i); - final Checksum[] checksums = mChecksums.valueAt(i); + final PerFileChecksum perFileChecksum = mChecksums.valueAt(i); + final Checksum[] checksums = perFileChecksum.getChecksums(); for (Checksum checksum : checksums) { out.startTag(null, TAG_SESSION_CHECKSUM); writeStringAttribute(out, ATTR_NAME, fileName); @@ -4286,6 +4349,19 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { out.endTag(null, TAG_SESSION_CHECKSUM); } } + for (int i = 0, isize = mChecksums.size(); i < isize; ++i) { + final String fileName = mChecksums.keyAt(i); + final PerFileChecksum perFileChecksum = mChecksums.valueAt(i); + final byte[] signature = perFileChecksum.getSignature(); + if (signature == null || signature.length == 0) { + continue; + } + out.startTag(null, TAG_SESSION_CHECKSUM_SIGNATURE); + writeStringAttribute(out, ATTR_NAME, fileName); + writeByteArrayAttribute(out, ATTR_SIGNATURE, signature); + out.endTag(null, TAG_SESSION_CHECKSUM_SIGNATURE); + } + } out.endTag(null, TAG_SESSION); @@ -4401,6 +4477,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { List<Integer> childSessionIds = new ArrayList<>(); List<InstallationFile> files = new ArrayList<>(); ArrayMap<String, List<Checksum>> checksums = new ArrayMap<>(); + ArrayMap<String, byte[]> signatures = new ArrayMap<>(); int outerDepth = in.getDepth(); int type; while ((type = in.next()) != XmlPullParser.END_DOCUMENT @@ -4443,6 +4520,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } fileChecksums.add(checksum); } + if (TAG_SESSION_CHECKSUM_SIGNATURE.equals(in.getName())) { + final String fileName = readStringAttribute(in, ATTR_NAME); + final byte[] signature = readByteArrayAttribute(in, ATTR_SIGNATURE); + signatures.put(fileName, signature); + } } if (grantedRuntimePermissions.size() > 0) { @@ -4471,13 +4553,25 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { fileArray = files.toArray(EMPTY_INSTALLATION_FILE_ARRAY); } + ArrayMap<String, PerFileChecksum> checksumsMap = null; + if (!checksums.isEmpty()) { + checksumsMap = new ArrayMap<>(checksums.size()); + for (int i = 0, isize = checksums.size(); i < isize; ++i) { + final String fileName = checksums.keyAt(i); + final List<Checksum> perFileChecksum = checksums.valueAt(i); + final byte[] perFileSignature = signatures.get(fileName); + checksumsMap.put(fileName, new PerFileChecksum( + perFileChecksum.toArray(new Checksum[perFileChecksum.size()]), + perFileSignature)); + } + } + InstallSource installSource = InstallSource.create(installInitiatingPackageName, installOriginatingPackageName, installerPackageName, installerAttributionTag); - return new PackageInstallerSession(callback, context, pm, sessionProvider, - installerThread, stagingManager, sessionId, userId, installerUid, - installSource, params, createdMillis, committedMillis, stageDir, stageCid, - fileArray, checksums, prepared, committed, destroyed, sealed, childSessionIdsArray, - parentSessionId, isReady, isFailed, isApplied, stagedSessionErrorCode, - stagedSessionErrorMessage); + return new PackageInstallerSession(callback, context, pm, sessionProvider, installerThread, + stagingManager, sessionId, userId, installerUid, installSource, params, + createdMillis, committedMillis, stageDir, stageCid, fileArray, checksumsMap, + prepared, committed, destroyed, sealed, childSessionIdsArray, parentSessionId, + isReady, isFailed, isApplied, stagedSessionErrorCode, stagedSessionErrorMessage); } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index de7338f3e440..c93127db7ca8 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -464,7 +464,7 @@ import java.util.function.Predicate; /** * Keep track of all those APKs everywhere. * <p> - * Internally there are two important locks: + * Internally there are three important locks: * <ul> * <li>{@link #mLock} is used to guard all in-memory parsed package details * and other related state. It is a fine-grained lock that should only be held @@ -475,6 +475,10 @@ import java.util.function.Predicate; * this lock should never be acquired while already holding {@link #mLock}. * Conversely, it's safe to acquire {@link #mLock} momentarily while already * holding {@link #mInstallLock}. + * <li>{@link #mSnapshotLock} is used to guard access to two snapshot fields: the snapshot + * itself and the snapshot invalidation flag. This lock should never be acquired while + * already holding {@link #mLock}. Conversely, it's safe to acquire {@link #mLock} + * momentarily while already holding {@link #mSnapshotLock}. * </ul> * Many internal methods rely on the caller to hold the appropriate locks, and * this contract is expressed through method name suffixes: @@ -485,6 +489,8 @@ import java.util.function.Predicate; * <li>fooLPr(): the caller must hold {@link #mLock} for reading * <li>fooLPw(): the caller must hold {@link #mLock} for writing * </ul> + * {@link #mSnapshotLock} is taken in exactly one place - {@code snapshotComputer()}. It + * should not be taken anywhere else or used for any other purpose. * <p> * Because this class is very central to the platform's security; please run all * CTS and unit tests whenever making modifications: @@ -4727,11 +4733,19 @@ public class PackageManagerService extends IPackageManager.Stub // If true, the cached computer object is invalid (the cache is stale). // The attribute is static since it may be set from outside classes. private static volatile boolean sSnapshotInvalid = true; - // If true, the cache is corked. Do not create a new cache but continue to use the + // If true, the cache is corked. Do not create a new cache but continue to use the // existing one. This throttles cache creation during periods of churn in Package // Manager. private static volatile boolean sSnapshotCorked = false; + /** + * This lock is used to make reads from {@link #sSnapshotInvalid} and + * {@link #mSnapshotComputer} atomic inside {@code snapshotComputer()}. This lock is + * not meant to be used outside that method. This lock must be taken before + * {@link #mLock} is taken. + */ + private final Object mSnapshotLock = new Object(); + // A counter of all queries that hit the cache. private AtomicInteger mSnapshotHits = new AtomicInteger(0); @@ -4759,35 +4773,42 @@ public class PackageManagerService extends IPackageManager.Stub if (!SNAPSHOT_ENABLED) { return mLiveComputer; } + if (Thread.holdsLock(mLock)) { + // If the current thread holds mLock then it may have modified state but not + // yet invalidated the snapshot. Always give the thread the live computer. + return mLiveComputer; + } int hits = 0; if (TRACE_CACHES) { hits = mSnapshotHits.incrementAndGet(); } - Computer c = mSnapshotComputer; - if (sSnapshotCorked && (c != null)) { - // Snapshots are corked, which means new ones should not be built right now. - return c; - } - if (sSnapshotInvalid || (c == null)) { - // The snapshot is invalid if it is marked as invalid or if it is null. If it - // is null, then it is currently being rebuilt by rebuildSnapshot(). - synchronized (mLock) { - // Rebuild the snapshot if it is invalid. Note that the snapshot might be - // invalidated as it is rebuilt. However, the snapshot is still - // self-consistent (the lock is being held)and is current as of the time - // this function is entered. - if (sSnapshotInvalid) { - rebuildSnapshot(hits); - } + synchronized (mSnapshotLock) { + Computer c = mSnapshotComputer; + if (sSnapshotCorked && (c != null)) { + // Snapshots are corked, which means new ones should not be built right now. + return c; + } + if (sSnapshotInvalid || (c == null)) { + // The snapshot is invalid if it is marked as invalid or if it is null. If it + // is null, then it is currently being rebuilt by rebuildSnapshot(). + synchronized (mLock) { + // Rebuild the snapshot if it is invalid. Note that the snapshot might be + // invalidated as it is rebuilt. However, the snapshot is still + // self-consistent (the lock is being held)and is current as of the time + // this function is entered. + if (sSnapshotInvalid) { + rebuildSnapshot(hits); + } - // Guaranteed to be non-null. mSnapshotComputer is only be set to null - // temporarily in rebuildSnapshot(), which is guarded by mLock(). Since - // the mLock is held in this block and since rebuildSnapshot() is - // complete, the attribute can not now be null. - c = mSnapshotComputer; + // Guaranteed to be non-null. mSnapshotComputer is only be set to null + // temporarily in rebuildSnapshot(), which is guarded by mLock(). Since + // the mLock is held in this block and since rebuildSnapshot() is + // complete, the attribute can not now be null. + c = mSnapshotComputer; + } } + return c; } - return c; } /** diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 9f07695fcecf..89729b585b14 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -1049,6 +1049,7 @@ class ShortcutPackage extends ShortcutPackageItem { } } + // TODO: update resource strings in AppSearch // If this shortcut is not from a manifest, then update all resource IDs // from resource names. (We don't allow resource strings for // non-manifest at the moment, but icons can still be resources.) @@ -1340,6 +1341,7 @@ class ShortcutPackage extends ShortcutPackageItem { * For all the text fields, refresh the string values if they're from resources. */ public void resolveResourceStrings() { + // TODO: update resource strings in AppSearch final ShortcutService s = mShortcutUser.mService; List<ShortcutInfo> changedShortcuts = null; diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 95ce140470f9..3c4457db6cf0 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -1565,6 +1565,7 @@ public class ShortcutService extends IShortcutService.Stub { * resource-based strings. */ void fixUpShortcutResourceNamesAndValues(ShortcutInfo si) { + // TODO: update resource names in AppSearch final Resources publisherRes = injectGetResourcesForApplicationAsUser( si.getPackage(), si.getUserId()); if (publisherRes != null) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 7caf739a9986..607c165c2543 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -39,6 +39,7 @@ import static android.view.Display.STATE_OFF; import static android.view.KeyEvent.KEYCODE_BACK; import static android.view.KeyEvent.KEYCODE_DPAD_CENTER; import static android.view.KeyEvent.KEYCODE_DPAD_DOWN; +import static android.view.KeyEvent.KEYCODE_HOME; import static android.view.KeyEvent.KEYCODE_POWER; import static android.view.KeyEvent.KEYCODE_UNKNOWN; import static android.view.KeyEvent.KEYCODE_VOLUME_DOWN; @@ -490,6 +491,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mWakeOnDpadKeyPress; boolean mWakeOnAssistKeyPress; boolean mWakeOnBackKeyPress; + long mWakeUpToLastStateTimeout; private boolean mHandleVolumeKeysInWM; @@ -1846,6 +1848,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { mPerDisplayFocusEnabled = mContext.getResources().getBoolean( com.android.internal.R.bool.config_perDisplayFocusEnabled); + mWakeUpToLastStateTimeout = mContext.getResources().getInteger( + com.android.internal.R.integer.config_wakeUpToLastStateTimeoutMillis); + readConfigurationDependentBehaviors(); mDisplayFoldController = DisplayFoldController.create(context, DEFAULT_DISPLAY); @@ -2600,7 +2605,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // can never break it, although if keyguard is on, we do let // it handle it, because that gives us the correct 5 second // timeout. - if (keyCode == KeyEvent.KEYCODE_HOME) { + if (keyCode == KEYCODE_HOME) { DisplayHomeButtonHandler handler = mDisplayHomeButtonHandlers.get(displayId); if (handler == null) { handler = new DisplayHomeButtonHandler(displayId); @@ -3556,8 +3561,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (isValidGlobalKey(keyCode) && mGlobalKeyManager.shouldHandleGlobalKey(keyCode, event)) { if (isWakeKey) { - wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, - PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY"); + wakeUpFromWakeKey(event); } return result; } @@ -3879,8 +3883,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (isWakeKey) { - wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, - PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY"); + wakeUpFromWakeKey(event); } if ((result & ACTION_PASS_TO_USER) != 0) { @@ -4319,9 +4322,39 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private boolean shouldWakeUpWithHomeIntent() { + if (mWakeUpToLastStateTimeout <= 0) { + return false; + } + + final long sleepDuration = mPowerManagerInternal.getLastWakeup().sleepDuration; + if (DEBUG_WAKEUP) { + Log.i(TAG, "shouldWakeUpWithHomeIntent: sleepDuration= " + sleepDuration + + " mWakeUpToLastStateTimeout= " + mWakeUpToLastStateTimeout); + } + return sleepDuration > mWakeUpToLastStateTimeout; + } + private void wakeUpFromPowerKey(long eventTime) { - wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey, - PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER"); + if (wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey, + PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER")) { + // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout + if (shouldWakeUpWithHomeIntent()) { + startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ false, /*wakenFromDreams*/ true, + PowerManager.wakeReasonToString(PowerManager.WAKE_REASON_POWER_BUTTON)); + } + } + } + + private void wakeUpFromWakeKey(KeyEvent event) { + if (wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, + PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY")) { + // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout + if (shouldWakeUpWithHomeIntent() && event.getKeyCode() == KEYCODE_HOME) { + startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ true, /*wakenFromDreams*/ true, + PowerManager.wakeReasonToString(PowerManager.WAKE_REASON_WAKE_KEY)); + } + } } private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason, @@ -5004,7 +5037,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { return null; } - void startDockOrHome(int displayId, boolean fromHomeKey, boolean awakenFromDreams) { + void startDockOrHome(int displayId, boolean fromHomeKey, boolean awakenFromDreams, + String startReason) { try { ActivityManager.getService().stopAppSwitches(); } catch (RemoteException e) {} @@ -5032,11 +5066,20 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + if (DEBUG_WAKEUP) { + Log.d(TAG, "startDockOrHome: startReason= " + startReason); + } + // Start home. - mActivityTaskManagerInternal.startHomeOnDisplay(mCurrentUserId, "startDockOrHome", + mActivityTaskManagerInternal.startHomeOnDisplay(mCurrentUserId, startReason, displayId, true /* allowInstrumenting */, fromHomeKey); } + void startDockOrHome(int displayId, boolean fromHomeKey, boolean awakenFromDreams) { + startDockOrHome(displayId, fromHomeKey, awakenFromDreams, /*startReason*/ + "startDockOrHome"); + } + /** * goes to the home screen * @return whether it did anything diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java index 0157706866c7..17e81daa80cc 100644 --- a/services/core/java/com/android/server/policy/WindowOrientationListener.java +++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java @@ -62,6 +62,7 @@ public abstract class WindowOrientationListener { private Sensor mSensor; private OrientationJudge mOrientationJudge; private int mCurrentRotation = -1; + private final Context mContext; private final Object mLock = new Object(); @@ -88,6 +89,7 @@ public abstract class WindowOrientationListener { * This constructor is private since no one uses it. */ private WindowOrientationListener(Context context, Handler handler, int rate) { + mContext = context; mHandler = handler; mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); mRate = rate; @@ -284,6 +286,19 @@ public abstract class WindowOrientationListener { } } + /** + * Returns whether this WindowOrientationListener can remain enabled while the device is dozing. + * If this returns true, it implies that the underlying sensor can still run while the AP is + * asleep, and that the underlying sensor will wake the AP on an event. + */ + public boolean shouldStayEnabledWhileDreaming() { + if (mContext.getResources().getBoolean( + com.android.internal.R.bool.config_forceOrientationListenerEnabledWhileDreaming)) { + return true; + } + return mSensor.getType() == Sensor.TYPE_DEVICE_ORIENTATION && mSensor.isWakeUpSensor(); + } + abstract class OrientationJudge implements SensorEventListener { // Number of nanoseconds per millisecond. protected static final long NANOS_PER_MS = 1000000; diff --git a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java deleted file mode 100644 index b95c5efca6d5..000000000000 --- a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.policy.role; - -import android.annotation.NonNull; -import android.annotation.UserIdInt; -import android.app.role.RoleManager; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; -import android.content.pm.ResolveInfo; -import android.provider.Settings; -import android.text.TextUtils; -import android.util.Slog; - -import com.android.internal.R; -import com.android.internal.telephony.SmsApplication; -import com.android.internal.util.CollectionUtils; -import com.android.server.LocalServices; -import com.android.server.role.LegacyRoleHolderProvider; -import com.android.server.role.RoleManagerService; - -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -/** - * Logic to retrieve the various legacy(pre-Q) equivalents of role holders. - * - * Unlike {@link RoleManagerService} this is meant to be pretty high-level to allow for depending - * on all kinds of various systems that are historically involved in legacy role resolution, - * e.g. {@link SmsApplication} - * - * @see RoleManagerService#migrateRoleIfNecessary - */ -public class LegacyRoleResolutionPolicy implements LegacyRoleHolderProvider { - - private static final boolean DEBUG = false; - private static final String LOG_TAG = "LegacyRoleResolutionPol"; - - @NonNull - private final Context mContext; - - public LegacyRoleResolutionPolicy(@NonNull Context context) { - mContext = context; - } - - @NonNull - @Override - public List<String> getLegacyRoleHolders(@NonNull String roleName, @UserIdInt int userId) { - switch (roleName) { - case RoleManager.ROLE_ASSISTANT: { - String packageName; - String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(), - Settings.Secure.ASSISTANT, userId); - // AssistUtils was using the default assistant app if Settings.Secure.ASSISTANT is - // null, while only an empty string means user selected "None". - if (setting != null) { - if (!setting.isEmpty()) { - ComponentName componentName = ComponentName.unflattenFromString(setting); - packageName = componentName != null ? componentName.getPackageName() : null; - } else { - packageName = null; - } - } else if (mContext.getPackageManager().isDeviceUpgrading()) { - String defaultAssistant = mContext.getString(R.string.config_defaultAssistant); - packageName = !TextUtils.isEmpty(defaultAssistant) ? defaultAssistant : null; - } else { - packageName = null; - } - return CollectionUtils.singletonOrEmpty(packageName); - } - case RoleManager.ROLE_BROWSER: { - PackageManagerInternal packageManagerInternal = LocalServices.getService( - PackageManagerInternal.class); - String packageName = packageManagerInternal.removeLegacyDefaultBrowserPackageName( - userId); - return CollectionUtils.singletonOrEmpty(packageName); - } - case RoleManager.ROLE_DIALER: { - String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(), - Settings.Secure.DIALER_DEFAULT_APPLICATION, userId); - String packageName; - if (!TextUtils.isEmpty(setting)) { - packageName = setting; - } else if (mContext.getPackageManager().isDeviceUpgrading()) { - // DefaultDialerManager was using the default dialer app if - // Settings.Secure.DIALER_DEFAULT_APPLICATION is invalid. - // TelecomManager.getSystemDialerPackage() won't work because it might not - // be ready. - packageName = mContext.getString(R.string.config_defaultDialer); - } else { - packageName = null; - } - return CollectionUtils.singletonOrEmpty(packageName); - } - case RoleManager.ROLE_SMS: { - String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(), - Settings.Secure.SMS_DEFAULT_APPLICATION, userId); - String packageName; - if (!TextUtils.isEmpty(setting)) { - packageName = setting; - } else if (mContext.getPackageManager().isDeviceUpgrading()) { - // SmsApplication was using the default SMS app if - // Settings.Secure.DIALER_DEFAULT_APPLICATION is invalid. - packageName = mContext.getString(R.string.config_defaultSms); - } else { - packageName = null; - } - return CollectionUtils.singletonOrEmpty(packageName); - } - case RoleManager.ROLE_HOME: { - PackageManager packageManager = mContext.getPackageManager(); - String packageName; - if (packageManager.isDeviceUpgrading()) { - ResolveInfo resolveInfo = packageManager.resolveActivityAsUser( - new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME), - PackageManager.MATCH_DEFAULT_ONLY - | PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); - packageName = resolveInfo != null && resolveInfo.activityInfo != null - ? resolveInfo.activityInfo.packageName : null; - if (packageName != null && isSettingsApplication(packageName, userId)) { - packageName = null; - } - } else { - packageName = null; - } - return CollectionUtils.singletonOrEmpty(packageName); - } - case RoleManager.ROLE_EMERGENCY: { - String defaultEmergencyApp = Settings.Secure.getStringForUser( - mContext.getContentResolver(), - Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION, userId); - return CollectionUtils.singletonOrEmpty(defaultEmergencyApp); - } - default: { - Slog.e(LOG_TAG, "Don't know how to find legacy role holders for " + roleName); - return Collections.emptyList(); - } - } - } - - private boolean isSettingsApplication(@NonNull String packageName, @UserIdInt int userId) { - PackageManager packageManager = mContext.getPackageManager(); - ResolveInfo resolveInfo = packageManager.resolveActivityAsUser(new Intent( - Settings.ACTION_SETTINGS), PackageManager.MATCH_DEFAULT_ONLY - | PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); - if (resolveInfo == null || resolveInfo.activityInfo == null) { - return false; - } - return Objects.equals(packageName, resolveInfo.activityInfo.packageName); - } -} diff --git a/services/core/java/com/android/server/policy/role/LegacyRoleStateProviderImpl.java b/services/core/java/com/android/server/policy/role/LegacyRoleStateProviderImpl.java new file mode 100644 index 000000000000..097f332a2637 --- /dev/null +++ b/services/core/java/com/android/server/policy/role/LegacyRoleStateProviderImpl.java @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.policy.role; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.role.RoleManager; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; +import android.content.pm.ResolveInfo; +import android.os.Environment; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.AtomicFile; +import android.util.Slog; +import android.util.Xml; + +import com.android.internal.R; +import com.android.server.LocalServices; +import com.android.server.role.LegacyRoleStateProvider; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * Implementation to provide legacy role state. + */ +public class LegacyRoleStateProviderImpl implements LegacyRoleStateProvider { + private static final String LOG_TAG = "LegacyRoleState"; + + private static final String ROLES_FILE_NAME = "roles.xml"; + + private static final String TAG_ROLES = "roles"; + private static final String TAG_ROLE = "role"; + private static final String TAG_HOLDER = "holder"; + private static final String ATTRIBUTE_NAME = "name"; + + @NonNull + private final Context mContext; + + public LegacyRoleStateProviderImpl(@NonNull Context context) { + mContext = context; + } + + @NonNull + @Override + public Map<String, Set<String>> getLegacyRoleState(@UserIdInt int userId) { + Map<String, Set<String>> roles = readFile(userId); + if (roles == null) { + roles = readFromLegacySettings(userId); + } + return roles; + } + + @Nullable + private Map<String, Set<String>> readFile(@UserIdInt int userId) { + File file = getFile(userId); + try (FileInputStream in = new AtomicFile(file).openRead()) { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(in, null); + Map<String, Set<String>> roles = parseXml(parser); + Slog.i(LOG_TAG, "Read legacy roles.xml successfully"); + return roles; + } catch (FileNotFoundException e) { + Slog.i(LOG_TAG, "Legacy roles.xml not found"); + return null; + } catch (XmlPullParserException | IOException e) { + Slog.wtf(LOG_TAG, "Failed to parse legacy roles.xml: " + file, e); + return null; + } + } + + @NonNull + private Map<String, Set<String>> parseXml(@NonNull XmlPullParser parser) throws IOException, + XmlPullParserException { + int type; + int depth; + int innerDepth = parser.getDepth() + 1; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { + if (depth > innerDepth || type != XmlPullParser.START_TAG) { + continue; + } + + if (parser.getName().equals(TAG_ROLES)) { + return parseRoles(parser); + } + } + + throw new IOException("Missing <" + TAG_ROLES + "> in roles.xml"); + } + + @NonNull + private Map<String, Set<String>> parseRoles(@NonNull XmlPullParser parser) throws IOException, + XmlPullParserException { + Map<String, Set<String>> roles = new ArrayMap<>(); + + int type; + int depth; + int innerDepth = parser.getDepth() + 1; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { + if (depth > innerDepth || type != XmlPullParser.START_TAG) { + continue; + } + + if (parser.getName().equals(TAG_ROLE)) { + String roleName = parser.getAttributeValue(null, ATTRIBUTE_NAME); + Set<String> roleHolders = parseRoleHoldersLocked(parser); + roles.put(roleName, roleHolders); + } + } + + return roles; + } + + @NonNull + private Set<String> parseRoleHoldersLocked(@NonNull XmlPullParser parser) + throws IOException, XmlPullParserException { + Set<String> roleHolders = new ArraySet<>(); + + int type; + int depth; + int innerDepth = parser.getDepth() + 1; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { + if (depth > innerDepth || type != XmlPullParser.START_TAG) { + continue; + } + + if (parser.getName().equals(TAG_HOLDER)) { + String roleHolder = parser.getAttributeValue(null, ATTRIBUTE_NAME); + roleHolders.add(roleHolder); + } + } + + return roleHolders; + } + + @NonNull + private static File getFile(@UserIdInt int userId) { + return new File(Environment.getUserSystemDirectory(userId), ROLES_FILE_NAME); + } + + @NonNull + private Map<String, Set<String>> readFromLegacySettings(@UserIdInt int userId) { + Map<String, Set<String>> roles = new ArrayMap<>(); + + // Assistant + ContentResolver contentResolver = mContext.getContentResolver(); + String assistantSetting = Settings.Secure.getStringForUser(contentResolver, + Settings.Secure.ASSISTANT, userId); + PackageManager packageManager = mContext.getPackageManager(); + String assistantPackageName; + // AssistUtils was using the default assistant app if Settings.Secure.ASSISTANT is + // null, while only an empty string means user selected "None". + if (assistantSetting != null) { + if (!assistantSetting.isEmpty()) { + ComponentName componentName = ComponentName.unflattenFromString(assistantSetting); + assistantPackageName = componentName != null ? componentName.getPackageName() + : null; + } else { + assistantPackageName = null; + } + } else if (packageManager.isDeviceUpgrading()) { + String defaultAssistant = mContext.getString(R.string.config_defaultAssistant); + assistantPackageName = !TextUtils.isEmpty(defaultAssistant) ? defaultAssistant : null; + } else { + assistantPackageName = null; + } + if (assistantPackageName != null) { + roles.put(RoleManager.ROLE_ASSISTANT, Collections.singleton(assistantPackageName)); + } + + // Browser + PackageManagerInternal packageManagerInternal = LocalServices.getService( + PackageManagerInternal.class); + String browserPackageName = packageManagerInternal.removeLegacyDefaultBrowserPackageName( + userId); + if (browserPackageName != null) { + roles.put(RoleManager.ROLE_BROWSER, Collections.singleton(browserPackageName)); + } + + // Dialer + String dialerSetting = Settings.Secure.getStringForUser(contentResolver, + Settings.Secure.DIALER_DEFAULT_APPLICATION, userId); + String dialerPackageName; + if (!TextUtils.isEmpty(dialerSetting)) { + dialerPackageName = dialerSetting; + } else if (packageManager.isDeviceUpgrading()) { + // DefaultDialerManager was using the default dialer app if + // Settings.Secure.DIALER_DEFAULT_APPLICATION is invalid. + // TelecomManager.getSystemDialerPackage() won't work because it might not + // be ready. + dialerPackageName = mContext.getString(R.string.config_defaultDialer); + } else { + dialerPackageName = null; + } + if (dialerPackageName != null) { + roles.put(RoleManager.ROLE_DIALER, Collections.singleton(dialerPackageName)); + } + + // SMS + String smsSetting = Settings.Secure.getStringForUser(contentResolver, + Settings.Secure.SMS_DEFAULT_APPLICATION, userId); + String smsPackageName; + if (!TextUtils.isEmpty(smsSetting)) { + smsPackageName = smsSetting; + } else if (mContext.getPackageManager().isDeviceUpgrading()) { + // SmsApplication was using the default SMS app if + // Settings.Secure.DIALER_DEFAULT_APPLICATION is invalid. + smsPackageName = mContext.getString(R.string.config_defaultSms); + } else { + smsPackageName = null; + } + if (smsPackageName != null) { + roles.put(RoleManager.ROLE_SMS, Collections.singleton(smsPackageName)); + } + + // Home + String homePackageName; + if (packageManager.isDeviceUpgrading()) { + ResolveInfo resolveInfo = packageManager.resolveActivityAsUser( + new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME), + PackageManager.MATCH_DEFAULT_ONLY + | PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); + homePackageName = resolveInfo != null && resolveInfo.activityInfo != null + ? resolveInfo.activityInfo.packageName : null; + if (homePackageName != null && isSettingsApplication(homePackageName, userId)) { + homePackageName = null; + } + } else { + homePackageName = null; + } + if (homePackageName != null) { + roles.put(RoleManager.ROLE_HOME, Collections.singleton(homePackageName)); + } + + // Emergency + String emergencyPackageName = Settings.Secure.getStringForUser(contentResolver, + Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION, userId); + if (emergencyPackageName != null) { + roles.put(RoleManager.ROLE_EMERGENCY, Collections.singleton(emergencyPackageName)); + } + + return roles; + } + + private boolean isSettingsApplication(@NonNull String packageName, @UserIdInt int userId) { + PackageManager packageManager = mContext.getPackageManager(); + ResolveInfo resolveInfo = packageManager.resolveActivityAsUser(new Intent( + Settings.ACTION_SETTINGS), PackageManager.MATCH_DEFAULT_ONLY + | PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); + if (resolveInfo == null || resolveInfo.activityInfo == null) { + return false; + } + return Objects.equals(packageName, resolveInfo.activityInfo.packageName); + } +} diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 955e1cdfeaaa..084dc32e8ad7 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -5570,7 +5570,7 @@ public final class PowerManagerService extends SystemService private PowerManager.WakeData getLastWakeupInternal() { synchronized (mLock) { - return new WakeData(mLastWakeTime, mLastWakeReason); + return new WakeData(mLastWakeTime, mLastWakeReason, mLastWakeTime - mLastSleepTime); } } diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java index 777857209de0..64bddcdd8fe1 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsService.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java @@ -19,14 +19,19 @@ package com.android.server.powerstats; import android.annotation.Nullable; import android.content.Context; import android.hardware.power.stats.ChannelInfo; +import android.hardware.power.stats.EnergyConsumerResult; import android.hardware.power.stats.PowerEntityInfo; import android.os.Binder; import android.os.Environment; +import android.os.Handler; +import android.os.HandlerThread; import android.os.UserHandle; +import android.power.PowerStatsInternal; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; +import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.SystemService; import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper; import com.android.server.powerstats.ProtoStreamUtils.ChannelInfoUtils; @@ -36,6 +41,7 @@ import com.android.server.powerstats.ProtoStreamUtils.PowerEntityInfoUtils; import java.io.File; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.concurrent.CompletableFuture; /** * This class provides a system service that estimates system power usage @@ -55,7 +61,6 @@ public class PowerStatsService extends SystemService { private final Injector mInjector; private Context mContext; - private IPowerStatsHALWrapper mPowerStatsHALWrapper; @Nullable private PowerStatsLogger mPowerStatsLogger; @Nullable @@ -65,6 +70,8 @@ public class PowerStatsService extends SystemService { @VisibleForTesting static class Injector { + private IPowerStatsHALWrapper mPowerStatsHALWrapper; + File createDataStoragePath() { return new File(Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM), DATA_STORAGE_SUBDIR); @@ -86,6 +93,13 @@ public class PowerStatsService extends SystemService { return PowerStatsHALWrapper.getPowerStatsHalImpl(); } + IPowerStatsHALWrapper getPowerStatsHALWrapperImpl() { + if (mPowerStatsHALWrapper == null) { + mPowerStatsHALWrapper = PowerStatsHALWrapper.getPowerStatsHalImpl(); + } + return mPowerStatsHALWrapper; + } + PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath, String meterFilename, String modelFilename, String residencyFilename, IPowerStatsHALWrapper powerStatsHALWrapper) { @@ -120,15 +134,15 @@ public class PowerStatsService extends SystemService { } } else if (args.length == 0) { pw.println("PowerStatsService dumpsys: available PowerEntityInfos"); - PowerEntityInfo[] powerEntityInfo = mPowerStatsHALWrapper.getPowerEntityInfo(); + PowerEntityInfo[] powerEntityInfo = getPowerStatsHal().getPowerEntityInfo(); PowerEntityInfoUtils.dumpsys(powerEntityInfo, pw); pw.println("PowerStatsService dumpsys: available ChannelInfos"); - ChannelInfo[] channelInfo = mPowerStatsHALWrapper.getEnergyMeterInfo(); + ChannelInfo[] channelInfo = getPowerStatsHal().getEnergyMeterInfo(); ChannelInfoUtils.dumpsys(channelInfo, pw); pw.println("PowerStatsService dumpsys: available EnergyConsumerIds"); - int[] energyConsumerId = mPowerStatsHALWrapper.getEnergyConsumerInfo(); + int[] energyConsumerId = getPowerStatsHal().getEnergyConsumerInfo(); EnergyConsumerIdUtils.dumpsys(energyConsumerId, pw); } } @@ -144,27 +158,33 @@ public class PowerStatsService extends SystemService { @Override public void onStart() { + if (getPowerStatsHal().isInitialized()) { + // Only create internal service if PowerStatsHal is available. + publishLocalService(PowerStatsInternal.class, new LocalService()); + } publishBinderService(Context.POWER_STATS_SERVICE, new BinderService()); } private void onSystemServiceReady() { - mPowerStatsHALWrapper = mInjector.createPowerStatsHALWrapperImpl(); - - if (mPowerStatsHALWrapper.isInitialized()) { - if (DEBUG) Slog.d(TAG, "Starting PowerStatsService"); + if (getPowerStatsHal().isInitialized()) { + if (DEBUG) Slog.d(TAG, "Starting PowerStatsService loggers"); // Only start logger and triggers if initialization is successful. mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, mInjector.createDataStoragePath(), mInjector.createMeterFilename(), mInjector.createModelFilename(), mInjector.createResidencyFilename(), - mPowerStatsHALWrapper); + getPowerStatsHal()); mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger); mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger); } else { - Slog.e(TAG, "Initialization of PowerStatsHAL wrapper failed"); + Slog.e(TAG, "Failed to start PowerStatsService loggers"); } } + private IPowerStatsHALWrapper getPowerStatsHal() { + return mInjector.getPowerStatsHALWrapperImpl(); + } + public PowerStatsService(Context context) { this(context, new Injector()); } @@ -175,4 +195,29 @@ public class PowerStatsService extends SystemService { mContext = context; mInjector = injector; } + + private final class LocalService extends PowerStatsInternal { + private final Handler mHandler; + + LocalService() { + HandlerThread thread = new HandlerThread(TAG); + thread.start(); + mHandler = new Handler(thread.getLooper()); + } + + @Override + public CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync( + int[] energyConsumerIds) { + final CompletableFuture<EnergyConsumerResult[]> future = new CompletableFuture<>(); + mHandler.sendMessage( + PooledLambda.obtainMessage(PowerStatsService.this::getEnergyConsumedAsync, + future, energyConsumerIds)); + return future; + } + } + + private void getEnergyConsumedAsync(CompletableFuture<EnergyConsumerResult[]> future, + int[] energyConsumerIds) { + future.complete(getPowerStatsHal().getEnergyConsumed(energyConsumerIds)); + } } diff --git a/services/core/java/com/android/server/role/LegacyRoleHolderProvider.java b/services/core/java/com/android/server/role/LegacyRoleStateProvider.java index ed0d67563132..ec4cfc176a8d 100644 --- a/services/core/java/com/android/server/role/LegacyRoleHolderProvider.java +++ b/services/core/java/com/android/server/role/LegacyRoleStateProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,22 +19,25 @@ package com.android.server.role; import android.annotation.NonNull; import android.annotation.UserIdInt; -import java.util.List; +import java.util.Map; +import java.util.Set; /** - * A provider for migrating legacy "role"s to their actual role implementation. + * Provider for legacy role state. + * <p> + * The role state may come from two sources, either the different pre-role default app settings, or + * the pre-modularization roles.xml file stored in platform. + * + * @hide */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) -public interface LegacyRoleHolderProvider { +public interface LegacyRoleStateProvider { /** - * Get the list of holders of a legacy "role" before its actual role is introduced. - * <p> - * This method will only be called for the first time a role is made available in the platform. + * Get the legacy role state stored in the platform. * - * @param roleName the name of the role * @param userId the user ID - * @return a list of holders for the given role + * @return a mapping of role name to its set of holders */ @NonNull - List<String> getLegacyRoleHolders(@NonNull String roleName, @UserIdInt int userId); + Map<String, Set<String>> getLegacyRoleState(@UserIdInt int userId); } diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index 4e42f16b68bf..bc5ddd3aae95 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -103,7 +103,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C private final Object mLock = new Object(); @NonNull - private final LegacyRoleHolderProvider mLegacyRoleHolderProvider; + private final LegacyRoleStateProvider mLegacyRoleStateProvider; /** * Maps user id to its state. @@ -139,10 +139,10 @@ public class RoleManagerService extends SystemService implements RoleUserState.C new SparseArray<>(); public RoleManagerService(@NonNull Context context, - @NonNull LegacyRoleHolderProvider legacyRoleHolderProvider) { + @NonNull LegacyRoleStateProvider legacyRoleStateProvider) { super(context); - mLegacyRoleHolderProvider = legacyRoleHolderProvider; + mLegacyRoleStateProvider = legacyRoleStateProvider; RoleControllerManager.initializeRemoteServiceComponentName(context); @@ -241,16 +241,6 @@ public class RoleManagerService extends SystemService implements RoleUserState.C return AndroidFuture.completedFuture(null); } - //TODO gradually add more role migrations statements here for remaining roles - // Make sure to implement LegacyRoleResolutionPolicy#getRoleHolders - // for a given role before adding a migration statement for it here - maybeMigrateRole(RoleManager.ROLE_ASSISTANT, userId); - maybeMigrateRole(RoleManager.ROLE_BROWSER, userId); - maybeMigrateRole(RoleManager.ROLE_DIALER, userId); - maybeMigrateRole(RoleManager.ROLE_SMS, userId); - maybeMigrateRole(RoleManager.ROLE_EMERGENCY, userId); - maybeMigrateRole(RoleManager.ROLE_HOME, userId); - // Some package state has changed, so grant default roles again. Slog.i(LOG_TAG, "Granting default roles..."); AndroidFuture<Void> future = new AndroidFuture<>(); @@ -266,23 +256,6 @@ public class RoleManagerService extends SystemService implements RoleUserState.C return future; } - private void maybeMigrateRole(String role, @UserIdInt int userId) { - // Any role for which we have a record are already migrated - RoleUserState userState = getOrCreateUserState(userId); - if (!userState.isRoleAvailable(role)) { - List<String> roleHolders = mLegacyRoleHolderProvider.getLegacyRoleHolders(role, userId); - if (roleHolders.isEmpty()) { - return; - } - Slog.i(LOG_TAG, "Migrating " + role + ", legacy holders: " + roleHolders); - userState.addRoleName(role); - int size = roleHolders.size(); - for (int i = 0; i < size; i++) { - userState.addRoleHolder(role, roleHolders.get(i)); - } - } - } - @Nullable private String computePackageStateHash(@UserIdInt int userId) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); @@ -327,7 +300,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C synchronized (mLock) { RoleUserState userState = mUserStates.get(userId); if (userState == null) { - userState = new RoleUserState(userId, this); + userState = new RoleUserState(userId, mLegacyRoleStateProvider, this); mUserStates.put(userId, userState); } return userState; @@ -663,7 +636,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C getContext().enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); } - if (mPackageManagerInternal.getInstantAppPackageName(callingUid) != null) { + if (isInstantApp(callingUid)) { return null; } @@ -676,6 +649,25 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } } + private boolean isInstantApp(int uid) { + final long identity = Binder.clearCallingIdentity(); + try { + final UserHandle user = UserHandle.getUserHandleForUid(uid); + final Context userContext = getContext().createContextAsUser(user, 0); + final PackageManager userPackageManager = userContext.getPackageManager(); + // Instant apps can not have shared UID, so it's safe to check only the first + // package name here. + final String packageName = ArrayUtils.firstOrNull( + userPackageManager.getPackagesForUid(uid)); + if (packageName == null) { + return false; + } + return userPackageManager.isInstantApp(packageName); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + @Override public boolean setBrowserRoleHolder(@Nullable String packageName, @UserIdInt int userId) { final Context context = getContext(); diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java index 3a5ed5c6425f..f5a79eabaf5a 100644 --- a/services/core/java/com/android/server/role/RoleUserState.java +++ b/services/core/java/com/android/server/role/RoleUserState.java @@ -21,14 +21,11 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.annotation.WorkerThread; -import android.os.Environment; import android.os.Handler; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.AtomicFile; import android.util.Slog; -import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BackgroundThread; @@ -38,13 +35,6 @@ import com.android.internal.util.function.pooled.PooledLambda; import com.android.role.persistence.RolesPersistence; import com.android.role.persistence.RolesState; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -59,23 +49,17 @@ class RoleUserState { public static final int VERSION_UNDEFINED = -1; - private static final String ROLES_FILE_NAME = "roles.xml"; - private static final long WRITE_DELAY_MILLIS = 200; - private static final String TAG_ROLES = "roles"; - private static final String TAG_ROLE = "role"; - private static final String TAG_HOLDER = "holder"; - private static final String ATTRIBUTE_VERSION = "version"; - private static final String ATTRIBUTE_NAME = "name"; - private static final String ATTRIBUTE_PACKAGES_HASH = "packagesHash"; - private final RolesPersistence mPersistence = RolesPersistence.createInstance(); @UserIdInt private final int mUserId; @NonNull + private final LegacyRoleStateProvider mLegacyStateProvider; + + @NonNull private final Callback mCallback; @NonNull @@ -108,10 +92,13 @@ class RoleUserState { * Create a new user state, and read its state from disk if previously persisted. * * @param userId the user id for this user state + * @param legacyStateProvider the provider for legacy role state * @param callback the callback for this user state */ - public RoleUserState(@UserIdInt int userId, @NonNull Callback callback) { + public RoleUserState(@UserIdInt int userId, + @NonNull LegacyRoleStateProvider legacyStateProvider, @NonNull Callback callback) { mUserId = userId; + mLegacyStateProvider = legacyStateProvider; mCallback = callback; readFile(); @@ -368,102 +355,27 @@ class RoleUserState { private void readFile() { synchronized (mLock) { - RolesState roles = mPersistence.readForUser(UserHandle.of(mUserId)); - if (roles == null) { - readLegacyFileLocked(); - scheduleWriteFileLocked(); - return; - } - - mVersion = roles.getVersion(); - mPackagesHash = roles.getPackagesHash(); + RolesState roleState = mPersistence.readForUser(UserHandle.of(mUserId)); + Map<String, Set<String>> roles; + if (roleState != null) { + mVersion = roleState.getVersion(); + mPackagesHash = roleState.getPackagesHash(); + roles = roleState.getRoles(); + } else { + roles = mLegacyStateProvider.getLegacyRoleState(mUserId); + } mRoles.clear(); - for (Map.Entry<String, Set<String>> entry : roles.getRoles().entrySet()) { + for (Map.Entry<String, Set<String>> entry : roles.entrySet()) { String roleName = entry.getKey(); ArraySet<String> roleHolders = new ArraySet<>(entry.getValue()); mRoles.put(roleName, roleHolders); } - } - } - - private void readLegacyFileLocked() { - File file = getFile(mUserId); - try (FileInputStream in = new AtomicFile(file).openRead()) { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(in, null); - parseXmlLocked(parser); - Slog.i(LOG_TAG, "Read roles.xml successfully"); - } catch (FileNotFoundException e) { - Slog.i(LOG_TAG, "roles.xml not found"); - } catch (XmlPullParserException | IOException e) { - throw new IllegalStateException("Failed to parse roles.xml: " + file, e); - } - } - - private void parseXmlLocked(@NonNull XmlPullParser parser) throws IOException, - XmlPullParserException { - int type; - int depth; - int innerDepth = parser.getDepth() + 1; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { - if (depth > innerDepth || type != XmlPullParser.START_TAG) { - continue; - } - - if (parser.getName().equals(TAG_ROLES)) { - parseRolesLocked(parser); - return; - } - } - Slog.w(LOG_TAG, "Missing <" + TAG_ROLES + "> in roles.xml"); - } - - private void parseRolesLocked(@NonNull XmlPullParser parser) throws IOException, - XmlPullParserException { - mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION)); - mPackagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH); - mRoles.clear(); - - int type; - int depth; - int innerDepth = parser.getDepth() + 1; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { - if (depth > innerDepth || type != XmlPullParser.START_TAG) { - continue; - } - - if (parser.getName().equals(TAG_ROLE)) { - String roleName = parser.getAttributeValue(null, ATTRIBUTE_NAME); - ArraySet<String> roleHolders = parseRoleHoldersLocked(parser); - mRoles.put(roleName, roleHolders); - } - } - } - @NonNull - private ArraySet<String> parseRoleHoldersLocked(@NonNull XmlPullParser parser) - throws IOException, XmlPullParserException { - ArraySet<String> roleHolders = new ArraySet<>(); - - int type; - int depth; - int innerDepth = parser.getDepth() + 1; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { - if (depth > innerDepth || type != XmlPullParser.START_TAG) { - continue; - } - - if (parser.getName().equals(TAG_HOLDER)) { - String roleHolder = parser.getAttributeValue(null, ATTRIBUTE_NAME); - roleHolders.add(roleHolder); + if (roleState == null) { + scheduleWriteFileLocked(); } } - - return roleHolders; } /** @@ -549,11 +461,6 @@ class RoleUserState { } } - @NonNull - private static File getFile(@UserIdInt int userId) { - return new File(Environment.getUserSystemDirectory(userId), ROLES_FILE_NAME); - } - /** * Callback for a user state. */ diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index 00ab973b9c90..7523671fb3a7 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -23,6 +23,7 @@ import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.view.InsetsState.InternalInsetsType; import android.view.WindowInsetsController.Appearance; +import android.view.WindowInsetsController.Behavior; import com.android.internal.view.AppearanceRegion; import com.android.server.notification.NotificationDelegate; @@ -84,7 +85,6 @@ public interface StatusBarManagerInternal { void startAssist(Bundle args); void onCameraLaunchGestureDetected(int source); - void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive); void setDisableFlags(int displayId, int flags, String cause); void toggleSplitScreen(); void appTransitionFinished(int displayId); @@ -128,9 +128,10 @@ public interface StatusBarManagerInternal { */ void onRecentsAnimationStateChanged(boolean running); - /** @see com.android.internal.statusbar.IStatusBar#onSystemBarAppearanceChanged */ - void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance, - AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme); + /** @see com.android.internal.statusbar.IStatusBar#onSystemBarAttributesChanged */ + void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + @Behavior int behavior, boolean isFullscreen); /** @see com.android.internal.statusbar.IStatusBar#showTransient */ void showTransient(int displayId, @InternalInsetsType int[] types); diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 3ee8dd7e9d81..6306c5cdd082 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -57,6 +57,7 @@ import android.util.Slog; import android.util.SparseArray; import android.view.InsetsState.InternalInsetsType; import android.view.WindowInsetsController.Appearance; +import android.view.WindowInsetsController.Behavior; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -302,11 +303,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override - public void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) { - StatusBarManagerService.this.topAppWindowChanged(displayId, isFullscreen, isImmersive); - } - - @Override public void setDisableFlags(int displayId, int flags, String cause) { StatusBarManagerService.this.setDisableFlags(displayId, flags, cause); } @@ -521,16 +517,15 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override - public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance, - AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { - final UiState state = getUiState(displayId); - if (!state.appearanceEquals(appearance, appearanceRegions, navbarColorManagedByIme)) { - state.setAppearance(appearance, appearanceRegions, navbarColorManagedByIme); - } + public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + @Behavior int behavior, boolean isFullscreen) { + getUiState(displayId).setBarAttributes(appearance, appearanceRegions, + navbarColorManagedByIme, behavior, isFullscreen); if (mBar != null) { try { - mBar.onSystemBarAppearanceChanged(displayId, appearance, appearanceRegions, - navbarColorManagedByIme); + mBar.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions, + navbarColorManagedByIme, behavior, isFullscreen); } catch (RemoteException ex) { } } } @@ -981,27 +976,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } } - /** - * Enables System UI to know whether the top app is fullscreen or not, and whether this app is - * in immersive mode or not. - */ - private void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) { - enforceStatusBar(); - - synchronized(mLock) { - getUiState(displayId).setFullscreen(isFullscreen); - getUiState(displayId).setImmersive(isImmersive); - mHandler.post(() -> { - if (mBar != null) { - try { - mBar.topAppWindowChanged(displayId, isFullscreen, isImmersive); - } catch (RemoteException ex) { - } - } - }); - } - } - @Override public void setImeWindowStatus(int displayId, final IBinder token, final int vis, final int backDisposition, final boolean showImeSwitcher, @@ -1068,8 +1042,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D private AppearanceRegion[] mAppearanceRegions = new AppearanceRegion[0]; private ArraySet<Integer> mTransientBarTypes = new ArraySet<>(); private boolean mNavbarColorManagedByIme = false; + private @Behavior int mBehavior; private boolean mFullscreen = false; - private boolean mImmersive = false; private int mDisabled1 = 0; private int mDisabled2 = 0; private int mImeWindowVis = 0; @@ -1077,25 +1051,14 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D private boolean mShowImeSwitcher = false; private IBinder mImeToken = null; - private void setAppearance(@Appearance int appearance, - AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { + private void setBarAttributes(@Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + @Behavior int behavior, boolean isFullscreen) { mAppearance = appearance; mAppearanceRegions = appearanceRegions; mNavbarColorManagedByIme = navbarColorManagedByIme; - } - - private boolean appearanceEquals(@Appearance int appearance, - AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { - if (mAppearance != appearance || mAppearanceRegions.length != appearanceRegions.length - || mNavbarColorManagedByIme != navbarColorManagedByIme) { - return false; - } - for (int i = appearanceRegions.length - 1; i >= 0; i--) { - if (!mAppearanceRegions[i].equals(appearanceRegions[i])) { - return false; - } - } - return true; + mBehavior = behavior; + mFullscreen = isFullscreen; } private void showTransient(@InternalInsetsType int[] types) { @@ -1110,14 +1073,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } } - private void setFullscreen(boolean isFullscreen) { - mFullscreen = isFullscreen; - } - - private void setImmersive(boolean isImmersive) { - mImmersive = isImmersive; - } - private int getDisabled1() { return mDisabled1; } @@ -1200,7 +1155,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D state.mAppearance, state.mAppearanceRegions, state.mImeWindowVis, state.mImeBackDisposition, state.mShowImeSwitcher, gatherDisableActionsLocked(mCurrentUserId, 2), state.mImeToken, - state.mNavbarColorManagedByIme, state.mFullscreen, state.mImmersive, + state.mNavbarColorManagedByIme, state.mBehavior, state.mFullscreen, transientBarTypes); } } diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java index 808d130c6d6f..eb4a0501a953 100644 --- a/services/core/java/com/android/server/storage/StorageSessionController.java +++ b/services/core/java/com/android/server/storage/StorageSessionController.java @@ -382,7 +382,11 @@ public final class StorageSessionController { } } + private static boolean isSupportedVolume(VolumeInfo vol) { + return isEmulatedOrPublic(vol) || vol.type == VolumeInfo.TYPE_STUB; + } + private boolean shouldHandle(@Nullable VolumeInfo vol) { - return !mIsResetting && (vol == null || isEmulatedOrPublic(vol)); + return !mIsResetting && (vol == null || isSupportedVolume(vol)); } } diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java index 93ba758d29d6..753b42b24556 100644 --- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java +++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java @@ -192,7 +192,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi request.getSystemTextClassifierMetadata(), /* verifyCallingPackage= */ true, /* attemptToBind= */ true, - service -> service.onSuggestSelection(sessionId, request, callback), + service -> service.onSuggestSelection(sessionId, request, wrap(callback)), "onSuggestSelection", callback); } @@ -1057,6 +1057,8 @@ public final class TextClassificationManagerService extends ITextClassifierServi rewriteTextClassificationIcons(result); } else if (parcelled instanceof ConversationActions) { rewriteConversationActionsIcons(result); + } else if (parcelled instanceof TextSelection) { + rewriteTextSelectionIcons(result); } else { // do nothing. } @@ -1067,10 +1069,32 @@ public final class TextClassificationManagerService extends ITextClassifierServi } } - private static void rewriteTextClassificationIcons(Bundle result) { - final TextClassification classification = TextClassifierService.getResponse(result); + private static void rewriteTextSelectionIcons(Bundle result) { + final TextSelection textSelection = TextClassifierService.getResponse(result); + if (textSelection.getTextClassification() == null) { + return; + } + TextClassification newTextClassification = + rewriteTextClassificationIcons(textSelection.getTextClassification()); + if (newTextClassification == null) { + return; + } + TextClassifierService.putResponse( + result, + textSelection.toBuilder() + .setTextClassification(newTextClassification) + .build()); + } + + /** + * Returns a new {@link TextClassification} if any modification is made, {@code null} + * otherwise. + */ + @Nullable + private static TextClassification rewriteTextClassificationIcons( + TextClassification textClassification) { boolean rewrite = false; - final List<RemoteAction> actions = classification.getActions(); + final List<RemoteAction> actions = textClassification.getActions(); final int size = actions.size(); final List<RemoteAction> validActions = new ArrayList<>(size); for (int i = 0; i < size; i++) { @@ -1084,13 +1108,21 @@ public final class TextClassificationManagerService extends ITextClassifierServi } validActions.add(validAction); } - if (rewrite) { - TextClassifierService.putResponse( - result, - classification.toBuilder() - .clearActions() - .addActions(validActions) - .build()); + return rewrite + ? textClassification + .toBuilder() + .clearActions() + .addActions(validActions) + .build() + : null; + } + + private static void rewriteTextClassificationIcons(Bundle result) { + final TextClassification classification = TextClassifierService.getResponse(result); + TextClassification newTextClassification = rewriteTextClassificationIcons( + classification); + if (newTextClassification != null) { + TextClassifierService.putResponse(result, newTextClassification); } } diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java index 42f12eb23d39..07725038255e 100644 --- a/services/core/java/com/android/server/tv/TvInputHal.java +++ b/services/core/java/com/android/server/tv/TvInputHal.java @@ -51,7 +51,8 @@ final class TvInputHal implements Handler.Callback { public interface Callback { void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs); void onDeviceUnavailable(int deviceId); - void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs); + void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs, + int cableConnectionStatus); void onFirstFrameCaptured(int deviceId, int streamId); } @@ -142,8 +143,9 @@ final class TvInputHal implements Handler.Callback { mHandler.obtainMessage(EVENT_DEVICE_UNAVAILABLE, deviceId, 0).sendToTarget(); } - private void streamConfigsChangedFromNative(int deviceId) { - mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, 0).sendToTarget(); + private void streamConfigsChangedFromNative(int deviceId, int cableConnectionStatus) { + mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, + cableConnectionStatus).sendToTarget(); } private void firstFrameCapturedFromNative(int deviceId, int streamId) { @@ -184,6 +186,7 @@ final class TvInputHal implements Handler.Callback { case EVENT_STREAM_CONFIGURATION_CHANGED: { TvStreamConfig[] configs; int deviceId = msg.arg1; + int cableConnectionStatus = msg.arg2; synchronized (mLock) { if (DEBUG) { Slog.d(TAG, "EVENT_STREAM_CONFIGURATION_CHANGED: deviceId = " + deviceId); @@ -191,7 +194,7 @@ final class TvInputHal implements Handler.Callback { retrieveStreamConfigsLocked(deviceId); configs = mStreamConfigs.get(deviceId); } - mCallback.onStreamConfigurationChanged(deviceId, configs); + mCallback.onStreamConfigurationChanged(deviceId, configs, cableConnectionStatus); break; } diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index a036bd196b23..38ae51fc2c1b 100755 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -156,6 +156,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { synchronized (mLock) { Connection connection = new Connection(info); connection.updateConfigsLocked(configs); + connection.updateCableConnectionStatusLocked(info.getCableConnectionStatus()); mConnections.put(info.getDeviceId(), connection); buildHardwareListLocked(); mHandler.obtainMessage( @@ -202,7 +203,8 @@ class TvInputHardwareManager implements TvInputHal.Callback { } @Override - public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) { + public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs, + int cableConnectionStatus) { synchronized (mLock) { Connection connection = mConnections.get(deviceId); if (connection == null) { @@ -211,12 +213,22 @@ class TvInputHardwareManager implements TvInputHal.Callback { return; } int previousConfigsLength = connection.getConfigsLengthLocked(); + int previousCableConnectionStatus = connection.getInputStateLocked(); connection.updateConfigsLocked(configs); String inputId = mHardwareInputIdMap.get(deviceId); - if (inputId != null - && (previousConfigsLength == 0) != (connection.getConfigsLengthLocked() == 0)) { - mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, - connection.getInputStateLocked(), 0, inputId).sendToTarget(); + if (inputId != null) { + if (connection.updateCableConnectionStatusLocked(cableConnectionStatus)) { + if (previousCableConnectionStatus != connection.getInputStateLocked()) { + mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, + connection.getInputStateLocked(), 0, inputId).sendToTarget(); + } + } else { + if ((previousConfigsLength == 0) + != (connection.getConfigsLengthLocked() == 0)) { + mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, + connection.getInputStateLocked(), 0, inputId).sendToTarget(); + } + } } ITvInputHardwareCallback callback = connection.getCallbackLocked(); if (callback != null) { @@ -625,7 +637,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { } private class Connection implements IBinder.DeathRecipient { - private final TvInputHardwareInfo mHardwareInfo; + private TvInputHardwareInfo mHardwareInfo; private TvInputInfo mInfo; private TvInputHardwareImpl mHardware = null; private ITvInputHardwareCallback mCallback; @@ -634,6 +646,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { private Integer mResolvedUserId = null; private Runnable mOnFirstFrameCaptured; private ResourceClientProfile mResourceClientProfile = null; + private boolean mIsCableConnectionStatusUpdated = false; public Connection(TvInputHardwareInfo hardwareInfo) { mHardwareInfo = hardwareInfo; @@ -736,6 +749,17 @@ class TvInputHardwareManager implements TvInputHal.Callback { + " }"; } + public boolean updateCableConnectionStatusLocked(int cableConnectionStatus) { + // Update connection status only if it's not default value + if (cableConnectionStatus != TvInputHardwareInfo.CABLE_CONNECTION_STATUS_UNKNOWN + || mIsCableConnectionStatusUpdated) { + mIsCableConnectionStatusUpdated = true; + mHardwareInfo = mHardwareInfo.toBuilder() + .cableConnectionStatus(cableConnectionStatus).build(); + } + return mIsCableConnectionStatusUpdated; + } + private int getConfigsLengthLocked() { return mConfigs == null ? 0 : mConfigs.length; } @@ -743,7 +767,9 @@ class TvInputHardwareManager implements TvInputHal.Callback { private int getInputStateLocked() { int configsLength = getConfigsLengthLocked(); if (configsLength > 0) { - return INPUT_STATE_CONNECTED; + if (!mIsCableConnectionStatusUpdated) { + return INPUT_STATE_CONNECTED; + } } switch (mHardwareInfo.getCableConnectionStatus()) { case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_CONNECTED: diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java index 072bdd2d1506..988582da53ea 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -23,6 +23,7 @@ import android.app.ActivityManager.RunningAppProcessInfo; import android.content.Context; import android.media.IResourceManagerService; import android.media.tv.TvInputManager; +import android.media.tv.tuner.TunerFrontendInfo; import android.media.tv.tunerresourcemanager.CasSessionRequest; import android.media.tv.tunerresourcemanager.IResourcesReclaimListener; import android.media.tv.tunerresourcemanager.ITunerResourceManager; @@ -30,7 +31,6 @@ import android.media.tv.tunerresourcemanager.ResourceClientProfile; import android.media.tv.tunerresourcemanager.TunerCiCamRequest; import android.media.tv.tunerresourcemanager.TunerDemuxRequest; import android.media.tv.tunerresourcemanager.TunerDescramblerRequest; -import android.media.tv.tunerresourcemanager.TunerFrontendInfo; import android.media.tv.tunerresourcemanager.TunerFrontendRequest; import android.media.tv.tunerresourcemanager.TunerLnbRequest; import android.media.tv.tunerresourcemanager.TunerResourceManager; @@ -578,7 +578,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } else { // Add a new fe resource FrontendResource newFe = new FrontendResource.Builder(infos[i].handle) - .type(infos[i].frontendType) + .type(infos[i].type) .exclusiveGroupId(infos[i].exclusiveGroupId) .build(); addFrontendResource(newFe); diff --git a/services/core/java/com/android/server/utils/quota/OWNERS b/services/core/java/com/android/server/utils/quota/OWNERS new file mode 100644 index 000000000000..a2943f39db24 --- /dev/null +++ b/services/core/java/com/android/server/utils/quota/OWNERS @@ -0,0 +1,4 @@ +dplotnikov@google.com +kwekua@google.com +omakoto@google.com +yamasani@google.com diff --git a/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java b/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java index edbc05802697..39687231c249 100644 --- a/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java +++ b/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java @@ -18,6 +18,7 @@ package com.android.server.vibrator; import android.content.Context; import android.hardware.input.InputManager; +import android.os.CombinedVibrationEffect; import android.os.Handler; import android.os.VibrationAttributes; import android.os.VibrationEffect; @@ -84,11 +85,21 @@ public final class InputDeviceDelegate implements InputManager.InputDeviceListen * * @return {@link #isAvailable()} */ - public boolean vibrateIfAvailable(int uid, String opPkg, VibrationEffect effect, + public boolean vibrateIfAvailable(int uid, String opPkg, CombinedVibrationEffect effect, String reason, VibrationAttributes attrs) { synchronized (mLock) { - for (int i = 0; i < mInputDeviceVibrators.size(); i++) { - mInputDeviceVibrators.valueAt(i).vibrate(uid, opPkg, effect, reason, attrs); + // TODO(b/159207608): Pass on the combined vibration once InputManager is merged + if (effect instanceof CombinedVibrationEffect.Mono) { + VibrationEffect e = ((CombinedVibrationEffect.Mono) effect).getEffect(); + if (e instanceof VibrationEffect.Prebaked) { + VibrationEffect fallback = ((VibrationEffect.Prebaked) e).getFallbackEffect(); + if (fallback != null) { + e = fallback; + } + } + for (int i = 0; i < mInputDeviceVibrators.size(); i++) { + mInputDeviceVibrators.valueAt(i).vibrate(uid, opPkg, e, reason, attrs); + } } return mInputDeviceVibrators.size() > 0; } diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java index b0266d025c08..fe3b03abc79b 100644 --- a/services/core/java/com/android/server/vibrator/Vibration.java +++ b/services/core/java/com/android/server/vibrator/Vibration.java @@ -18,6 +18,7 @@ package com.android.server.vibrator; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.CombinedVibrationEffect; import android.os.IBinder; import android.os.SystemClock; import android.os.VibrationAttributes; @@ -72,14 +73,14 @@ public class Vibration { /** The actual effect to be played. */ @Nullable - private VibrationEffect mEffect; + private CombinedVibrationEffect mEffect; /** * The original effect that was requested. Typically these two things differ because the effect * was scaled based on the users vibration intensity settings. */ @Nullable - private VibrationEffect mOriginalEffect; + private CombinedVibrationEffect mOriginalEffect; /** * Start/end times in unix epoch time. Only to be used for debugging purposes and to correlate @@ -90,7 +91,7 @@ public class Vibration { private long mEndTimeDebug; private Status mStatus; - public Vibration(IBinder token, int id, VibrationEffect effect, + public Vibration(IBinder token, int id, CombinedVibrationEffect effect, VibrationAttributes attrs, int uid, String opPkg, String reason) { this.token = token; this.mEffect = effect; @@ -124,7 +125,7 @@ public class Vibration { * Replace this vibration effect if given {@code scaledEffect} is different, preserving the * original one for debug purposes. */ - public void updateEffect(@NonNull VibrationEffect newEffect) { + public void updateEffect(@NonNull CombinedVibrationEffect newEffect) { if (newEffect.equals(mEffect)) { return; } @@ -139,7 +140,7 @@ public class Vibration { /** Return the effect that should be played by this vibration. */ @Nullable - public VibrationEffect getEffect() { + public CombinedVibrationEffect getEffect() { return mEffect; } @@ -154,8 +155,8 @@ public class Vibration { public static final class DebugInfo { private final long mStartTimeDebug; private final long mEndTimeDebug; - private final VibrationEffect mEffect; - private final VibrationEffect mOriginalEffect; + private final CombinedVibrationEffect mEffect; + private final CombinedVibrationEffect mOriginalEffect; private final float mScale; private final VibrationAttributes mAttrs; private final int mUid; @@ -163,8 +164,8 @@ public class Vibration { private final String mReason; private final Status mStatus; - public DebugInfo(long startTimeDebug, long endTimeDebug, VibrationEffect effect, - VibrationEffect originalEffect, float scale, VibrationAttributes attrs, + public DebugInfo(long startTimeDebug, long endTimeDebug, CombinedVibrationEffect effect, + CombinedVibrationEffect originalEffect, float scale, VibrationAttributes attrs, int uid, String opPkg, String reason, Status status) { mStartTimeDebug = startTimeDebug; mEndTimeDebug = endTimeDebug; @@ -228,7 +229,22 @@ public class Vibration { proto.end(token); } - private void dumpEffect(ProtoOutputStream proto, long fieldId, VibrationEffect effect) { + private void dumpEffect( + ProtoOutputStream proto, long fieldId, CombinedVibrationEffect combinedEffect) { + VibrationEffect effect; + // TODO(b/177805090): add proper support for dumping combined effects to proto + if (combinedEffect instanceof CombinedVibrationEffect.Mono) { + effect = ((CombinedVibrationEffect.Mono) combinedEffect).getEffect(); + } else if (combinedEffect instanceof CombinedVibrationEffect.Stereo) { + effect = ((CombinedVibrationEffect.Stereo) combinedEffect).getEffects().valueAt(0); + } else if (combinedEffect instanceof CombinedVibrationEffect.Sequential) { + dumpEffect(proto, fieldId, + ((CombinedVibrationEffect.Sequential) combinedEffect).getEffects().get(0)); + return; + } else { + // Unknown combined effect, skip dump. + return; + } final long token = proto.start(fieldId); if (effect instanceof VibrationEffect.OneShot) { dumpEffect(proto, VibrationEffectProto.ONESHOT, (VibrationEffect.OneShot) effect); diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java index 5f7e47d6ca29..0fa4fe16e1ba 100644 --- a/services/core/java/com/android/server/vibrator/VibrationScaler.java +++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java @@ -18,12 +18,16 @@ package com.android.server.vibrator; import android.content.Context; import android.hardware.vibrator.V1_0.EffectStrength; +import android.os.CombinedVibrationEffect; import android.os.IExternalVibratorService; import android.os.VibrationEffect; import android.os.Vibrator; import android.util.Slog; import android.util.SparseArray; +import java.util.List; +import java.util.Objects; + /** Controls vibration scaling. */ // TODO(b/159207608): Make this package-private once vibrator services are moved to this package public final class VibrationScaler { @@ -87,6 +91,43 @@ public final class VibrationScaler { } /** + * Scale a {@link CombinedVibrationEffect} based on the given usage hint for this vibration. + * + * @param combinedEffect the effect to be scaled + * @param usageHint one of VibrationAttributes.USAGE_* + * @return The same given effect, if no changes were made, or a new + * {@link CombinedVibrationEffect} with resolved and scaled amplitude + */ + public <T extends CombinedVibrationEffect> T scale(CombinedVibrationEffect combinedEffect, + int usageHint) { + if (combinedEffect instanceof CombinedVibrationEffect.Mono) { + VibrationEffect effect = ((CombinedVibrationEffect.Mono) combinedEffect).getEffect(); + return (T) CombinedVibrationEffect.createSynced(scale(effect, usageHint)); + } else if (combinedEffect instanceof CombinedVibrationEffect.Stereo) { + SparseArray<VibrationEffect> effects = + ((CombinedVibrationEffect.Stereo) combinedEffect).getEffects(); + CombinedVibrationEffect.SyncedCombination combination = + CombinedVibrationEffect.startSynced(); + for (int i = 0; i < effects.size(); i++) { + combination.addVibrator(effects.keyAt(i), scale(effects.valueAt(i), usageHint)); + } + return (T) combination.combine(); + } else if (combinedEffect instanceof CombinedVibrationEffect.Sequential) { + List<CombinedVibrationEffect> effects = + ((CombinedVibrationEffect.Sequential) combinedEffect).getEffects(); + CombinedVibrationEffect.SequentialCombination combination = + CombinedVibrationEffect.startSequential(); + for (CombinedVibrationEffect effect : effects) { + combination.addNext(scale(effect, usageHint)); + } + return (T) combination.combine(); + } else { + // Unknown combination, return same effect. + return (T) combinedEffect; + } + } + + /** * Scale a {@link VibrationEffect} based on the given usage hint for this vibration. * * @param effect the effect to be scaled @@ -100,13 +141,23 @@ public final class VibrationScaler { int intensity = mSettingsController.getCurrentIntensity(usageHint); int newStrength = intensityToEffectStrength(intensity); VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect; - - if (prebaked.getEffectStrength() == newStrength) { + int strength = prebaked.getEffectStrength(); + VibrationEffect fallback = prebaked.getFallbackEffect(); + + if (fallback != null) { + VibrationEffect scaledFallback = scale(fallback, usageHint); + if (strength == newStrength && Objects.equals(fallback, scaledFallback)) { + return (T) prebaked; + } + + return (T) new VibrationEffect.Prebaked(prebaked.getId(), newStrength, + scaledFallback); + } else if (strength == newStrength) { return (T) prebaked; + } else { + return (T) new VibrationEffect.Prebaked(prebaked.getId(), prebaked.shouldFallback(), + newStrength); } - - return (T) new VibrationEffect.Prebaked( - prebaked.getId(), prebaked.shouldFallback(), newStrength); } effect = effect.resolve(mDefaultVibrationAmplitude); @@ -124,8 +175,6 @@ public final class VibrationScaler { return effect.scale(scale.factor); } - - /** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */ private static int intensityToEffectStrength(int intensity) { switch (intensity) { diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java new file mode 100644 index 000000000000..a4d888b3f9cf --- /dev/null +++ b/services/core/java/com/android/server/vibrator/VibrationThread.java @@ -0,0 +1,791 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import android.annotation.Nullable; +import android.os.CombinedVibrationEffect; +import android.os.IBinder; +import android.os.PowerManager; +import android.os.Process; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.Trace; +import android.os.VibrationEffect; +import android.os.WorkSource; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.IBatteryStats; +import com.android.internal.util.FrameworkStatsLog; + +import com.google.android.collect.Lists; + +import java.util.ArrayList; +import java.util.List; +import java.util.PriorityQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** Plays a {@link Vibration} in dedicated thread. */ +// TODO(b/159207608): Make this package-private once vibrator services are moved to this package +public final class VibrationThread extends Thread implements IBinder.DeathRecipient { + private static final String TAG = "VibrationThread"; + private static final boolean DEBUG = false; + + /** + * Extra timeout added to the end of each synced vibration step as a timeout for the callback + * wait, to ensure it finishes even when callbacks from individual vibrators are lost. + */ + private static final long CALLBACKS_EXTRA_TIMEOUT = 100; + + /** Callbacks for playing a {@link Vibration}. */ + public interface VibrationCallbacks { + + /** + * Callback triggered before starting a synchronized vibration step. This will be called + * with {@code requiredCapabilities = 0} if no synchronization is required. + * + * @param requiredCapabilities The required syncing capabilities for this preparation step. + * Expects a combination of values from + * IVibratorManager.CAP_PREPARE_* and + * IVibratorManager.CAP_MIXED_TRIGGER_*. + * @param vibratorIds The id of the vibrators to be prepared. + */ + void prepareSyncedVibration(int requiredCapabilities, int[] vibratorIds); + + /** Callback triggered after synchronized vibrations were prepared. */ + void triggerSyncedVibration(long vibrationId); + + /** Callback triggered when vibration thread is complete. */ + void onVibrationEnded(long vibrationId, Vibration.Status status); + } + + private final Object mLock = new Object(); + private final WorkSource mWorkSource = new WorkSource(); + private final PowerManager.WakeLock mWakeLock; + private final IBatteryStats mBatteryStatsService; + private final Vibration mVibration; + private final VibrationCallbacks mCallbacks; + private final SparseArray<VibratorController> mVibrators; + + @GuardedBy("mLock") + @Nullable + private VibrateStep mCurrentVibrateStep; + @GuardedBy("this") + private boolean mForceStop; + + // TODO(b/159207608): Remove this constructor once VibratorService is removed + public VibrationThread(Vibration vib, VibratorController vibrator, + PowerManager.WakeLock wakeLock, IBatteryStats batteryStatsService, + VibrationCallbacks callbacks) { + this(vib, toSparseArray(vibrator), wakeLock, batteryStatsService, callbacks); + } + + public VibrationThread(Vibration vib, SparseArray<VibratorController> availableVibrators, + PowerManager.WakeLock wakeLock, IBatteryStats batteryStatsService, + VibrationCallbacks callbacks) { + mVibration = vib; + mCallbacks = callbacks; + mWakeLock = wakeLock; + mWorkSource.set(vib.uid); + mWakeLock.setWorkSource(mWorkSource); + mBatteryStatsService = batteryStatsService; + + CombinedVibrationEffect effect = vib.getEffect(); + mVibrators = new SparseArray<>(); + for (int i = 0; i < availableVibrators.size(); i++) { + if (effect.hasVibrator(availableVibrators.keyAt(i))) { + mVibrators.put(availableVibrators.keyAt(i), availableVibrators.valueAt(i)); + } + } + } + + @Override + public void binderDied() { + cancel(); + } + + @Override + public void run() { + Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY); + mWakeLock.acquire(); + try { + mVibration.token.linkToDeath(this, 0); + Vibration.Status status = playVibration(); + mCallbacks.onVibrationEnded(mVibration.id, status); + } catch (RemoteException e) { + Slog.e(TAG, "Error linking vibration to token death", e); + } finally { + mVibration.token.unlinkToDeath(this, 0); + mWakeLock.release(); + } + } + + /** Cancel current vibration and shuts down the thread gracefully. */ + public void cancel() { + synchronized (this) { + mForceStop = true; + notify(); + } + } + + /** Notify current vibration that a step has completed on given vibrator. */ + public void vibratorComplete(int vibratorId) { + synchronized (mLock) { + if (mCurrentVibrateStep != null) { + mCurrentVibrateStep.vibratorComplete(vibratorId); + } + } + } + + @VisibleForTesting + SparseArray<VibratorController> getVibrators() { + return mVibrators; + } + + private Vibration.Status playVibration() { + Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration"); + try { + List<Step> steps = generateSteps(mVibration.getEffect()); + if (steps.isEmpty()) { + // No vibrator matching any incoming vibration effect. + return Vibration.Status.IGNORED; + } + Vibration.Status status = Vibration.Status.FINISHED; + final int stepCount = steps.size(); + for (int i = 0; i < stepCount; i++) { + Step step = steps.get(i); + synchronized (mLock) { + if (step instanceof VibrateStep) { + mCurrentVibrateStep = (VibrateStep) step; + } else { + mCurrentVibrateStep = null; + } + } + status = step.play(); + if (status != Vibration.Status.FINISHED) { + // This step was ignored by the vibrators, probably effects were unsupported. + break; + } + if (mForceStop) { + break; + } + } + if (mForceStop) { + return Vibration.Status.CANCELLED; + } + return status; + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); + } + } + + private List<Step> generateSteps(CombinedVibrationEffect effect) { + if (effect instanceof CombinedVibrationEffect.Sequential) { + CombinedVibrationEffect.Sequential sequential = + (CombinedVibrationEffect.Sequential) effect; + List<Step> steps = new ArrayList<>(); + final int sequentialEffectCount = sequential.getEffects().size(); + for (int i = 0; i < sequentialEffectCount; i++) { + int delay = sequential.getDelays().get(i); + if (delay > 0) { + steps.add(new DelayStep(delay)); + } + steps.addAll(generateSteps(sequential.getEffects().get(i))); + } + final int stepCount = steps.size(); + for (int i = 0; i < stepCount; i++) { + if (steps.get(i) instanceof VibrateStep) { + return steps; + } + } + // No valid vibrate step was generated, ignore effect completely. + return Lists.newArrayList(); + } + VibrateStep vibrateStep = null; + if (effect instanceof CombinedVibrationEffect.Mono) { + vibrateStep = createVibrateStep(mapToAvailableVibrators( + ((CombinedVibrationEffect.Mono) effect).getEffect())); + } else if (effect instanceof CombinedVibrationEffect.Stereo) { + vibrateStep = createVibrateStep(filterByAvailableVibrators( + ((CombinedVibrationEffect.Stereo) effect).getEffects())); + } + return vibrateStep == null ? Lists.newArrayList() : Lists.newArrayList(vibrateStep); + } + + @Nullable + private VibrateStep createVibrateStep(SparseArray<VibrationEffect> effects) { + if (effects.size() == 0) { + return null; + } + if (effects.size() == 1) { + // Create simplified step that handles a single vibrator. + return new SingleVibrateStep(mVibrators.get(effects.keyAt(0)), effects.valueAt(0)); + } + return new SyncedVibrateStep(effects); + } + + private SparseArray<VibrationEffect> mapToAvailableVibrators(VibrationEffect effect) { + SparseArray<VibrationEffect> mappedEffects = new SparseArray<>(mVibrators.size()); + for (int i = 0; i < mVibrators.size(); i++) { + mappedEffects.put(mVibrators.keyAt(i), effect); + } + return mappedEffects; + } + + private SparseArray<VibrationEffect> filterByAvailableVibrators( + SparseArray<VibrationEffect> effects) { + SparseArray<VibrationEffect> filteredEffects = new SparseArray<>(); + for (int i = 0; i < effects.size(); i++) { + if (mVibrators.contains(effects.keyAt(i))) { + filteredEffects.put(effects.keyAt(i), effects.valueAt(i)); + } + } + return filteredEffects; + } + + private static SparseArray<VibratorController> toSparseArray(VibratorController controller) { + SparseArray<VibratorController> array = new SparseArray<>(1); + array.put(controller.getVibratorInfo().getId(), controller); + return array; + } + + /** + * Get the duration the vibrator will be on for given {@code waveform}, starting at {@code + * startIndex} until the next time it's vibrating amplitude is zero. + */ + private static long getVibratorOnDuration(VibrationEffect.Waveform waveform, int startIndex) { + long[] timings = waveform.getTimings(); + int[] amplitudes = waveform.getAmplitudes(); + int repeatIndex = waveform.getRepeatIndex(); + int i = startIndex; + long timing = 0; + while (timings[i] == 0 || amplitudes[i] != 0) { + timing += timings[i++]; + if (i >= timings.length) { + if (repeatIndex >= 0) { + i = repeatIndex; + // prevent infinite loop + repeatIndex = -1; + } else { + break; + } + } + if (i == startIndex) { + return 1000; + } + } + return timing; + } + + /** + * Sleeps until given {@code wakeUpTime}. + * + * <p>This stops immediately when {@link #cancel()} is called. + */ + private void waitUntil(long wakeUpTime) { + synchronized (this) { + long durationRemaining = wakeUpTime - SystemClock.uptimeMillis(); + while (durationRemaining > 0) { + try { + VibrationThread.this.wait(durationRemaining); + } catch (InterruptedException e) { + } + if (mForceStop) { + break; + } + durationRemaining = wakeUpTime - SystemClock.uptimeMillis(); + } + } + } + + /** + * Sleeps until given {@link CountDownLatch} has finished or {@code wakeUpTime} was reached. + * + * <p>This stops immediately when {@link #cancel()} is called. + */ + private void awaitUntil(CountDownLatch counter, long wakeUpTime) { + synchronized (this) { + long durationRemaining = wakeUpTime - SystemClock.uptimeMillis(); + while (counter.getCount() > 0 && durationRemaining > 0) { + try { + counter.await(durationRemaining, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + } + if (mForceStop) { + break; + } + durationRemaining = wakeUpTime - SystemClock.uptimeMillis(); + } + } + } + + private void noteVibratorOn(long duration) { + try { + mBatteryStatsService.noteVibratorOn(mVibration.uid, duration); + FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED, + mVibration.uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON, + duration); + } catch (RemoteException e) { + } + } + + private void noteVibratorOff() { + try { + mBatteryStatsService.noteVibratorOff(mVibration.uid); + FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED, + mVibration.uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF, + /* duration= */ 0); + } catch (RemoteException e) { + } + } + + /** Represent a single synchronized step while playing a {@link CombinedVibrationEffect}. */ + private interface Step { + Vibration.Status play(); + } + + /** Represent a synchronized vibration step. */ + private interface VibrateStep extends Step { + /** Callback to notify a vibrator has finished playing a effect. */ + void vibratorComplete(int vibratorId); + } + + /** Represent a vibration on a single vibrator. */ + private final class SingleVibrateStep implements VibrateStep { + private final VibratorController mVibrator; + private final VibrationEffect mEffect; + private final CountDownLatch mCounter; + + SingleVibrateStep(VibratorController vibrator, VibrationEffect effect) { + mVibrator = vibrator; + mEffect = effect; + mCounter = new CountDownLatch(1); + } + + @Override + public void vibratorComplete(int vibratorId) { + if (mVibrator.getVibratorInfo().getId() != vibratorId) { + return; + } + if (mEffect instanceof VibrationEffect.OneShot + || mEffect instanceof VibrationEffect.Waveform) { + // Oneshot and Waveform are controlled by amplitude steps, ignore callbacks. + return; + } + mVibrator.off(); + mCounter.countDown(); + } + + @Override + public Vibration.Status play() { + Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "SingleVibrateStep"); + long duration = -1; + try { + if (DEBUG) { + Slog.d(TAG, "SingleVibrateStep starting..."); + } + long startTime = SystemClock.uptimeMillis(); + duration = vibratePredefined(mEffect); + + if (duration > 0) { + noteVibratorOn(duration); + // Vibration is playing with no need to control amplitudes, just wait for native + // callback or timeout. + awaitUntil(mCounter, startTime + duration + CALLBACKS_EXTRA_TIMEOUT); + return Vibration.Status.FINISHED; + } + + startTime = SystemClock.uptimeMillis(); + AmplitudeStep amplitudeStep = vibrateWithAmplitude(mEffect, startTime); + if (amplitudeStep == null) { + // Vibration could not be played with or without amplitude steps. + return Vibration.Status.IGNORED_UNSUPPORTED; + } + + duration = mEffect instanceof VibrationEffect.Prebaked + ? ((VibrationEffect.Prebaked) mEffect).getFallbackEffect().getDuration() + : mEffect.getDuration(); + if (duration < Long.MAX_VALUE) { + // Only report vibration stats if we know how long we will be vibrating. + noteVibratorOn(duration); + } + while (amplitudeStep != null) { + waitUntil(amplitudeStep.startTime); + if (mForceStop) { + mVibrator.off(); + return Vibration.Status.CANCELLED; + } + amplitudeStep.play(); + amplitudeStep = amplitudeStep.nextStep(); + } + + return Vibration.Status.FINISHED; + } finally { + if (duration > 0 && duration < Long.MAX_VALUE) { + noteVibratorOff(); + } + if (DEBUG) { + Slog.d(TAG, "SingleVibrateStep step done."); + } + Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); + } + } + + /** + * Try to vibrate given effect using prebaked or composed predefined effects. + * + * @return the duration, in millis, expected for the vibration, or -1 if effect cannot be + * played with predefined effects. + */ + private long vibratePredefined(VibrationEffect effect) { + if (effect instanceof VibrationEffect.Prebaked) { + VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect; + long duration = mVibrator.on(prebaked, mVibration.id); + if (duration > 0) { + return duration; + } + if (prebaked.getFallbackEffect() != null) { + return vibratePredefined(prebaked.getFallbackEffect()); + } + } else if (effect instanceof VibrationEffect.Composed) { + VibrationEffect.Composed composed = (VibrationEffect.Composed) effect; + return mVibrator.on(composed, mVibration.id); + } + // OneShot and Waveform effects require amplitude change after calling vibrator.on. + return -1; + } + + /** + * Try to vibrate given effect using {@link AmplitudeStep} to control vibration amplitude. + * + * @return the {@link AmplitudeStep} to start this vibration, or {@code null} if vibration + * do not require amplitude control. + */ + private AmplitudeStep vibrateWithAmplitude(VibrationEffect effect, long startTime) { + int vibratorId = mVibrator.getVibratorInfo().getId(); + if (effect instanceof VibrationEffect.OneShot) { + VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect; + return new AmplitudeStep(vibratorId, oneShot, startTime, startTime); + } else if (effect instanceof VibrationEffect.Waveform) { + VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect; + return new AmplitudeStep(vibratorId, waveform, startTime, startTime); + } else if (effect instanceof VibrationEffect.Prebaked) { + VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect; + if (prebaked.getFallbackEffect() != null) { + return vibrateWithAmplitude(prebaked.getFallbackEffect(), startTime); + } + } + return null; + } + } + + /** Represent a synchronized vibration step on multiple vibrators. */ + private final class SyncedVibrateStep implements VibrateStep { + private final SparseArray<VibrationEffect> mEffects; + private final CountDownLatch mActiveVibratorCounter; + + private final int mRequiredCapabilities; + private final int[] mVibratorIds; + + SyncedVibrateStep(SparseArray<VibrationEffect> effects) { + mEffects = effects; + mActiveVibratorCounter = new CountDownLatch(mEffects.size()); + // TODO(b/159207608): Calculate required capabilities for syncing this step. + mRequiredCapabilities = 0; + mVibratorIds = new int[effects.size()]; + for (int i = 0; i < effects.size(); i++) { + mVibratorIds[i] = effects.keyAt(i); + } + } + + @Override + public void vibratorComplete(int vibratorId) { + VibrationEffect effect = mEffects.get(vibratorId); + if (effect == null) { + return; + } + if (effect instanceof VibrationEffect.OneShot + || effect instanceof VibrationEffect.Waveform) { + // Oneshot and Waveform are controlled by amplitude steps, ignore callbacks. + return; + } + mVibrators.get(vibratorId).off(); + mActiveVibratorCounter.countDown(); + } + + @Override + public Vibration.Status play() { + Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "SyncedVibrateStep"); + long timeout = -1; + try { + if (DEBUG) { + Slog.d(TAG, "SyncedVibrateStep starting..."); + } + final PriorityQueue<AmplitudeStep> nextSteps = new PriorityQueue<>(mEffects.size()); + long startTime = SystemClock.uptimeMillis(); + mCallbacks.prepareSyncedVibration(mRequiredCapabilities, mVibratorIds); + timeout = startVibrating(startTime, nextSteps); + mCallbacks.triggerSyncedVibration(mVibration.id); + noteVibratorOn(timeout); + + while (!nextSteps.isEmpty()) { + AmplitudeStep step = nextSteps.poll(); + waitUntil(step.startTime); + if (mForceStop) { + stopAllVibrators(); + return Vibration.Status.CANCELLED; + } + step.play(); + AmplitudeStep nextStep = step.nextStep(); + if (nextStep == null) { + // This vibrator has finished playing the effect for this step. + mActiveVibratorCounter.countDown(); + } else { + nextSteps.add(nextStep); + } + } + + // All OneShot and Waveform effects have finished. Just wait for the other effects + // to end via native callbacks before finishing this synced step. + awaitUntil(mActiveVibratorCounter, startTime + timeout + CALLBACKS_EXTRA_TIMEOUT); + return Vibration.Status.FINISHED; + } finally { + if (timeout > 0) { + noteVibratorOff(); + } + if (DEBUG) { + Slog.d(TAG, "SyncedVibrateStep done."); + } + Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); + } + } + + /** + * Starts playing effects on designated vibrators. + * + * <p>This includes the {@link VibrationEffect.OneShot} and {@link VibrationEffect.Waveform} + * effects, that should start in sync with all other effects in this step. The waveforms are + * controlled by {@link AmplitudeStep} added to the {@code nextSteps} queue. + * + * @return A duration, in millis, to wait for the completion of all vibrations. This ignores + * any repeating waveform duration and returns the duration of a single run. + */ + private long startVibrating(long startTime, PriorityQueue<AmplitudeStep> nextSteps) { + long maxDuration = 0; + for (int i = 0; i < mEffects.size(); i++) { + VibratorController controller = mVibrators.get(mEffects.keyAt(i)); + VibrationEffect effect = mEffects.valueAt(i); + maxDuration = Math.max(maxDuration, + startVibrating(controller, effect, startTime, nextSteps)); + } + return maxDuration; + } + + /** + * Play a single effect on a single vibrator. + * + * @return A duration, in millis, to wait for the completion of this effect. This ignores + * any repeating waveform duration and returns the duration of a single run to be used as + * timeout for callbacks. + */ + private long startVibrating(VibratorController controller, VibrationEffect effect, + long startTime, PriorityQueue<AmplitudeStep> nextSteps) { + int vibratorId = controller.getVibratorInfo().getId(); + long duration; + if (effect instanceof VibrationEffect.OneShot) { + VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect; + duration = oneShot.getDuration(); + controller.on(duration, mVibration.id); + nextSteps.add( + new AmplitudeStep(vibratorId, oneShot, startTime, startTime + duration)); + } else if (effect instanceof VibrationEffect.Waveform) { + VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect; + duration = getVibratorOnDuration(waveform, 0); + if (duration > 0) { + // Waveform starts by turning vibrator on. Do it in this sync vibrate step. + controller.on(duration, mVibration.id); + } + nextSteps.add( + new AmplitudeStep(vibratorId, waveform, startTime, startTime + duration)); + } else if (effect instanceof VibrationEffect.Prebaked) { + VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect; + duration = controller.on(prebaked, mVibration.id); + if (duration <= 0 && prebaked.getFallbackEffect() != null) { + return startVibrating(controller, prebaked.getFallbackEffect(), startTime, + nextSteps); + } + } else if (effect instanceof VibrationEffect.Composed) { + VibrationEffect.Composed composed = (VibrationEffect.Composed) effect; + duration = controller.on(composed, mVibration.id); + } else { + duration = 0; + } + return duration; + } + + private void stopAllVibrators() { + for (int vibratorId : mVibratorIds) { + VibratorController controller = mVibrators.get(vibratorId); + if (controller != null) { + controller.off(); + } + } + } + } + + /** Represent a step to set amplitude on a single vibrator. */ + private final class AmplitudeStep implements Step, Comparable<AmplitudeStep> { + public final int vibratorId; + public final VibrationEffect.Waveform waveform; + public final int currentIndex; + public final long startTime; + public final long vibratorStopTime; + + AmplitudeStep(int vibratorId, VibrationEffect.OneShot oneShot, + long startTime, long vibratorStopTime) { + this(vibratorId, (VibrationEffect.Waveform) VibrationEffect.createWaveform( + new long[]{oneShot.getDuration()}, + new int[]{oneShot.getAmplitude()}, /* repeat= */ -1), + startTime, + vibratorStopTime); + } + + AmplitudeStep(int vibratorId, VibrationEffect.Waveform waveform, + long startTime, long vibratorStopTime) { + this(vibratorId, waveform, /* index= */ 0, startTime, vibratorStopTime); + } + + AmplitudeStep(int vibratorId, VibrationEffect.Waveform waveform, + int index, long startTime, long vibratorStopTime) { + this.vibratorId = vibratorId; + this.waveform = waveform; + this.currentIndex = index; + this.startTime = startTime; + this.vibratorStopTime = vibratorStopTime; + } + + @Override + public Vibration.Status play() { + Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "AmplitudeStep"); + try { + if (DEBUG) { + Slog.d(TAG, "AmplitudeStep starting on vibrator " + vibratorId + "..."); + } + VibratorController controller = mVibrators.get(vibratorId); + if (currentIndex < 0) { + controller.off(); + if (DEBUG) { + Slog.d(TAG, "Vibrator turned off and finishing"); + } + return Vibration.Status.FINISHED; + } + if (waveform.getTimings()[currentIndex] == 0) { + // Skip waveform entries with zero timing. + return Vibration.Status.FINISHED; + } + int amplitude = waveform.getAmplitudes()[currentIndex]; + if (amplitude == 0) { + controller.off(); + if (DEBUG) { + Slog.d(TAG, "Vibrator turned off"); + } + return Vibration.Status.FINISHED; + } + if (startTime >= vibratorStopTime) { + // Vibrator has stopped. Turn vibrator back on for the duration of another + // cycle before setting the amplitude. + long onDuration = getVibratorOnDuration(waveform, currentIndex); + if (onDuration > 0) { + controller.on(onDuration, mVibration.id); + if (DEBUG) { + Slog.d(TAG, "Vibrator turned on for " + onDuration + "ms"); + } + } + } + controller.setAmplitude(amplitude); + if (DEBUG) { + Slog.d(TAG, "Amplitude changed to " + amplitude); + } + return Vibration.Status.FINISHED; + } finally { + if (DEBUG) { + Slog.d(TAG, "AmplitudeStep done."); + } + Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); + } + } + + @Override + public int compareTo(AmplitudeStep o) { + return Long.compare(startTime, o.startTime); + } + + /** Return next {@link AmplitudeStep} from this waveform, of {@code null} if finished. */ + @Nullable + public AmplitudeStep nextStep() { + if (currentIndex < 0) { + // Waveform has ended, no more steps to run. + return null; + } + long nextWakeUpTime = startTime + waveform.getTimings()[currentIndex]; + int nextIndex = currentIndex + 1; + if (nextIndex >= waveform.getTimings().length) { + nextIndex = waveform.getRepeatIndex(); + } + return new AmplitudeStep(vibratorId, waveform, nextIndex, nextWakeUpTime, + nextVibratorStopTime()); + } + + /** Return next time the vibrator will stop after this step is played. */ + private long nextVibratorStopTime() { + if (currentIndex < 0 || waveform.getTimings()[currentIndex] == 0 + || startTime < vibratorStopTime) { + return vibratorStopTime; + } + return startTime + getVibratorOnDuration(waveform, currentIndex); + } + } + + /** Represent a delay step with fixed duration, that starts counting when it starts playing. */ + private final class DelayStep implements Step { + private final int mDelay; + + DelayStep(int delay) { + mDelay = delay; + } + + @Override + public Vibration.Status play() { + Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "DelayStep"); + try { + if (DEBUG) { + Slog.d(TAG, "DelayStep of " + mDelay + "ms starting..."); + } + waitUntil(SystemClock.uptimeMillis() + mDelay); + return Vibration.Status.FINISHED; + } finally { + if (DEBUG) { + Slog.d(TAG, "DelayStep done."); + } + Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); + } + } + } +} diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java index 311c73bcb19f..53f52e286fbd 100644 --- a/services/core/java/com/android/server/vibrator/VibratorController.java +++ b/services/core/java/com/android/server/vibrator/VibratorController.java @@ -142,6 +142,11 @@ public final class VibratorController { } } + @VisibleForTesting + public NativeWrapper getNativeWrapper() { + return mNativeWrapper; + } + /** Return the {@link VibratorInfo} representing the vibrator controlled by this instance. */ public VibratorInfo getVibratorInfo() { return mVibratorInfo; @@ -240,6 +245,8 @@ public final class VibratorController { * {@link OnVibrationCompleteListener}. * * <p>This will affect the state of {@link #isVibrating()}. + * + * @return The duration of the effect playing, or 0 if unsupported. */ public long on(VibrationEffect.Prebaked effect, long vibrationId) { synchronized (mLock) { @@ -257,15 +264,20 @@ public final class VibratorController { * {@link OnVibrationCompleteListener}. * * <p>This will affect the state of {@link #isVibrating()}. + * + * @return The duration of the effect playing, or 0 if unsupported. */ - public void on(VibrationEffect.Composed effect, long vibrationId) { + public long on(VibrationEffect.Composed effect, long vibrationId) { if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) { - return; + return 0; } synchronized (mLock) { mNativeWrapper.compose(effect.getPrimitiveEffects().toArray( new VibrationEffect.Composition.PrimitiveEffect[0]), vibrationId); notifyVibratorOnLocked(); + // Compose don't actually give us an estimated duration, so we just guess here. + // TODO(b/177807015): use exposed durations from IVibrator here instead + return 20 * effect.getPrimitiveEffects().size(); } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 9554f2e6f8bf..f9cc33453e15 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -294,6 +294,7 @@ import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.view.WindowManager.TransitionOldType; import android.view.animation.Animation; +import android.window.IRemoteTransition; import android.window.TaskSnapshot; import android.window.WindowContainerToken; @@ -467,6 +468,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private ActivityOptions mPendingOptions; /** Non-null if {@link #mPendingOptions} specifies the remote animation. */ private RemoteAnimationAdapter mPendingRemoteAnimation; + private IRemoteTransition mPendingRemoteTransition; ActivityOptions returningOptions; // options that are coming back via convertToTranslucent AppTimeTracker appTimeTracker; // set if we are tracking the time in this app/task/activity ActivityServiceConnectionsHolder mServiceConnectionsHolder; // Service connections. @@ -894,6 +896,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A pw.print("pendingRemoteAnimationCallingPid="); pw.println(mPendingRemoteAnimation.getCallingPid()); } + if (mPendingRemoteTransition != null) { + pw.print(prefix + " pendingRemoteTransition=" + mPendingRemoteTransition); + } if (appTimeTracker != null) { appTimeTracker.dumpWithHeader(pw, prefix, false); } @@ -3244,6 +3249,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Override void removeImmediately() { + if (!finishing) { + // If Task#removeImmediately is called directly with alive activities, ensure that the + // activities are destroyed and detached from process. + destroyImmediately("removeImmediately"); + } onRemovedFromDisplay(); super.removeImmediately(); } @@ -3884,6 +3894,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (options.getAnimationType() == ANIM_REMOTE_ANIMATION) { mPendingRemoteAnimation = options.getRemoteAnimationAdapter(); } + mPendingRemoteTransition = options.getRemoteTransition(); } void applyOptionsAnimation() { @@ -4064,6 +4075,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A void clearOptionsAnimation() { mPendingOptions = null; mPendingRemoteAnimation = null; + mPendingRemoteTransition = null; } ActivityOptions getOptions() { @@ -4078,6 +4090,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return opts; } + IRemoteTransition takeRemoteTransition() { + IRemoteTransition out = mPendingRemoteTransition; + mPendingRemoteTransition = null; + return out; + } + boolean allowMoveToFront() { return mPendingOptions == null || !mPendingOptions.getAvoidMoveToFront(); } @@ -4773,7 +4791,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A void makeVisibleIfNeeded(ActivityRecord starting, boolean reportToClient) { // This activity is not currently visible, but is running. Tell it to become visible. - if (mState == RESUMED || this == starting) { + if ((mState == RESUMED && mVisibleRequested) || this == starting) { if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "Not making visible, r=" + this + " state=" + mState + " starting=" + starting); return; @@ -6479,8 +6497,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mLastReportedConfiguration.setConfiguration(global, override); } - boolean hasCompatDisplayInsets() { - return mCompatDisplayInsets != null; + @Nullable + CompatDisplayInsets getCompatDisplayInsets() { + return mCompatDisplayInsets; } /** @@ -7803,13 +7822,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** * The precomputed insets of the display in each rotation. This is used to make the size * compatibility mode activity compute the configuration without relying on its current display. - * This currently only supports fullscreen and freeform windowing mode. */ static class CompatDisplayInsets { + /** The container width on rotation 0. */ private final int mWidth; + /** The container height on rotation 0. */ private final int mHeight; + /** Whether the {@link Task} windowingMode represents a floating window*/ final boolean mIsFloating; - + /** Whether the {@link Task} is letterboxed when the unresizable activity is first shown. */ + final boolean mIsTaskLetterboxed; /** * The nonDecorInsets for each rotation. Includes the navigation bar and cutout insets. It * is used to compute the appBounds. @@ -7836,27 +7858,24 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mNonDecorInsets[rotation] = emptyRect; mStableInsets[rotation] = emptyRect; } + mIsTaskLetterboxed = false; return; } final Task task = container.getTask(); - if (task != null && task.isTaskLetterboxed()) { - // For apps in Task letterbox, it should fill the task bounds. - final Point dimensions = getRotationZeroDimensions(task); - mWidth = dimensions.x; - mHeight = dimensions.y; - } else { - // If the activity is not floating nor letterboxed, assume it fills the root. - final RootDisplayArea root = container.getRootDisplayArea(); - if (root == null || root == display) { - mWidth = display.mBaseDisplayWidth; - mHeight = display.mBaseDisplayHeight; - } else { - final Point dimensions = getRotationZeroDimensions(root); - mWidth = dimensions.x; - mHeight = dimensions.y; - } - } + mIsTaskLetterboxed = task != null && task.isTaskLetterboxed(); + + // Store the bounds of the Task for the non-resizable activity to use in size compat + // mode so that the activity will not be resized regardless the windowing mode it is + // currently in. + final WindowContainer filledContainer = task != null ? task : display; + final Point dimensions = getRotationZeroDimensions(filledContainer); + mWidth = dimensions.x; + mHeight = dimensions.y; + + // Bounds of the filled container if it doesn't fill the display. + final Rect unfilledContainerBounds = + filledContainer.getBounds().equals(display.getBounds()) ? null : new Rect(); final DisplayPolicy policy = display.getDisplayPolicy(); for (int rotation = 0; rotation < 4; rotation++) { mNonDecorInsets[rotation] = new Rect(); @@ -7869,6 +7888,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A policy.getNonDecorInsetsLw(rotation, dw, dh, cutout, mNonDecorInsets[rotation]); mStableInsets[rotation].set(mNonDecorInsets[rotation]); policy.convertNonDecorInsetsToStableInsets(mStableInsets[rotation], rotation); + + if (unfilledContainerBounds == null) { + continue; + } + // The insets is based on the display, but the container may be smaller than the + // display, so update the insets to exclude parts that are not intersected with the + // container. + unfilledContainerBounds.set(filledContainer.getBounds()); + display.rotateBounds( + filledContainer.getConfiguration().windowConfiguration.getRotation(), + rotation, + unfilledContainerBounds); + updateInsetsForBounds(unfilledContainerBounds, dw, dh, mNonDecorInsets[rotation]); + updateInsetsForBounds(unfilledContainerBounds, dw, dh, mStableInsets[rotation]); } } @@ -7886,6 +7919,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return rotated ? new Point(height, width) : new Point(width, height); } + /** + * Updates the display insets to exclude the parts that are not intersected with the given + * bounds. + */ + private static void updateInsetsForBounds(Rect bounds, int displayWidth, int displayHeight, + Rect inset) { + inset.left = Math.max(0, inset.left - bounds.left); + inset.top = Math.max(0, inset.top - bounds.top); + inset.right = Math.max(0, bounds.right - displayWidth + inset.right); + inset.bottom = Math.max(0, bounds.bottom - displayHeight + inset.bottom); + } + void getBoundsByRotation(Rect outBounds, int rotation) { final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); final int dw = rotated ? mHeight : mWidth; diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 20625c83a924..e67210e42cc8 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -110,6 +110,7 @@ import android.util.ArraySet; import android.util.DebugUtils; import android.util.Pools.SynchronizedPool; import android.util.Slog; +import android.window.IRemoteTransition; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.HeavyWeightSwitcherActivity; @@ -1573,6 +1574,10 @@ class ActivityStarter { final Transition newTransition = (!mService.getTransitionController().isCollecting() && mService.getTransitionController().getTransitionPlayer() != null) ? mService.getTransitionController().createTransition(TRANSIT_OPEN) : null; + IRemoteTransition remoteTransition = r.takeRemoteTransition(); + if (newTransition != null && remoteTransition != null) { + newTransition.setRemoteTransition(remoteTransition); + } mService.getTransitionController().collect(r); try { mService.deferWindowLayout(); @@ -1611,7 +1616,7 @@ class ActivityStarter { } if (newTransition != null) { mService.getTransitionController().requestStartTransition(newTransition, - r.getTask()); + mTargetTask, remoteTransition); } else { // Make the collecting transition wait until this request is ready. mService.getTransitionController().setReady(false); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 3c06488e73c3..b332739820dc 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -2674,8 +2674,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { .setWindowingMode(rootTask.getWindowingMode()) .setActivityType(rootTask.getActivityType()) .setActivityInfo(ainfo) - .setParent(rootTask.getDisplayArea()) .setIntent(intent) + .setTaskId(rootTask.getDisplayArea().getNextRootTaskId()) .build(); if (!mRecentTasks.addToBottom(task)) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 599bf3748dd9..a68f55750c18 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -1365,7 +1365,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { mUserLeaving = true; } - mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_TO_FRONT, task); + mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_TO_FRONT, + 0 /* flags */, task, options != null ? options.getRemoteTransition() : null); reason = reason + " findTaskToMoveToFront"; boolean reparented = false; if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index d4629d9f9b8f..976924459252 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -664,8 +664,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // Used in updating override configurations private final Configuration mTempConfig = new Configuration(); - // Used in performing layout - private boolean mTmpWindowsBehindIme; + // Used in performing layout, to record the insets provided by other windows above the current + // window. + private InsetsState mTmpAboveInsetsState = new InsetsState(); /** * Used to prevent recursions when calling @@ -765,17 +766,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp + " parentHidden=" + w.isParentWindowHidden()); } - // Sets mBehindIme for each window. Windows behind IME can get IME insets. - if (w.mBehindIme != mTmpWindowsBehindIme) { - w.mBehindIme = mTmpWindowsBehindIme; - if (getInsetsStateController().getRawInsetsState().getSourceOrDefaultVisibility( - ITYPE_IME)) { - // If IME is invisible, behind IME or not doesn't make the insets different. - mWinInsetsChanged.add(w); - } - } - if (w == mInputMethodWindow) { - mTmpWindowsBehindIme = true; + // Sets mAboveInsets for each window. Windows behind the window providing the insets can + // receive the insets. + if (!w.mAboveInsetsState.equals(mTmpAboveInsetsState)) { + w.mAboveInsetsState.set(mTmpAboveInsetsState); + mWinInsetsChanged.add(w); } // If this view is GONE, then skip it -- keep the current frame, and let the caller know @@ -811,8 +806,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp + " mContainingFrame=" + w.getContainingFrame() + " mDisplayFrame=" + w.getDisplayFrame()); } + provideInsetsByWindow(w); }; + private void provideInsetsByWindow(WindowState w) { + for (int i = 0; i < w.mProvidedInsetsSources.size(); i++) { + final InsetsSource providedSource = w.mProvidedInsetsSources.valueAt(i); + mTmpAboveInsetsState.addSource(providedSource); + } + } + private final Consumer<WindowState> mPerformLayoutAttached = w -> { if (w.mLayoutAttached) { if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + w + " mHaveFrame=" + w.mHaveFrame @@ -3643,7 +3646,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp && mImeLayeringTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN // An activity with override bounds should be letterboxed inside its parent bounds, // so it doesn't fill the screen. - && mImeLayeringTarget.mActivityRecord.matchParentBounds(); + && mImeLayeringTarget.mActivityRecord.matchParentBounds() + // IME is attached to non-Letterboxed app windows, other than windows with + // LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER flag. (Refer to WS.isLetterboxedAppWindow()) + && mImeLayeringTarget.matchesRootDisplayAreaBounds(); } /** @@ -4191,6 +4197,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp calculateDisplayCutoutForRotation(mDisplayInfo.rotation)); mDisplayPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode); + // Used to indicate that we have processed the insets windows. This needs to be after + // beginLayoutLw to ensure the raw insets state display related info is initialized. + final InsetsState rawInsetsState = getInsetsStateController().getRawInsetsState(); + mTmpAboveInsetsState = new InsetsState(); + mTmpAboveInsetsState.setDisplayFrame(rawInsetsState.getDisplayFrame()); + mTmpAboveInsetsState.setDisplayCutout(rawInsetsState.getDisplayCutout()); + mTmpAboveInsetsState.mirrorAlwaysVisibleInsetsSources(rawInsetsState); + int seq = mLayoutSeq + 1; if (seq < 0) seq = 0; mLayoutSeq = seq; @@ -4200,8 +4214,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mTmpWindow = null; mTmpInitial = initial; - // Used to indicate that we have processed the IME window. - mTmpWindowsBehindIme = false; // First perform layout of any root windows (not attached to another window). forAllWindows(mPerformLayout, true /* traverseTopToBottom */); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index fb005b33f1e2..d9bc619fa45f 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -42,10 +42,6 @@ import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; -import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON; @@ -121,7 +117,6 @@ import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.Region; -import android.hardware.input.InputManager; import android.hardware.power.Boost; import android.os.Handler; import android.os.IBinder; @@ -136,16 +131,10 @@ import android.util.Slog; import android.util.SparseArray; import android.view.DisplayCutout; import android.view.Gravity; -import android.view.InputChannel; -import android.view.InputDevice; -import android.view.InputEvent; -import android.view.InputEventReceiver; import android.view.InsetsFlags; import android.view.InsetsSource; import android.view.InsetsState; import android.view.InsetsState.InternalInsetsType; -import android.view.MotionEvent; -import android.view.PointerIcon; import android.view.Surface; import android.view.View; import android.view.ViewDebug; @@ -334,7 +323,6 @@ public class DisplayPolicy { // What we last reported to system UI about whether the focused window is fullscreen/immersive. private boolean mLastFocusIsFullscreen = false; - private boolean mLastFocusIsImmersive = false; // If nonzero, a panic gesture was performed at that time in uptime millis and is still pending. private long mPendingPanicGestureUptime; @@ -363,9 +351,6 @@ public class DisplayPolicy { private boolean mDreamingLockscreen; private boolean mAllowLockscreenWhenOn; - @VisibleForTesting - EventReceiverInputConsumer mInputConsumer; - private PointerLocationView mPointerLocationView; /** @@ -380,6 +365,14 @@ public class DisplayPolicy { private RefreshRatePolicy mRefreshRatePolicy; + /** + * If true, attach the navigation bar to the current transition app. + * The value is read from config_attachNavBarToAppDuringTransition and could be overlaid by RRO + * when the navigation bar mode is changed. + */ + private boolean mShouldAttachNavBarToAppDuringTransition; + private NavBarFadeAnimationController mNavBarFadeAnimationController; + // -------- PolicyHandler -------- private static final int MSG_REQUEST_TRANSIENT_BARS = 2; private static final int MSG_DISPOSE_INPUT_CONSUMER = 3; @@ -1086,6 +1079,7 @@ public class DisplayPolicy { break; case TYPE_NAVIGATION_BAR: mNavigationBar = win; + updateNavBarFadeController(); mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win, (displayFrames, windowState, inOutFrame) -> { @@ -1231,6 +1225,7 @@ public class DisplayPolicy { mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, null, null); } else if (mNavigationBar == win || mNavigationBarAlt == win) { mNavigationBar = null; + updateNavBarFadeController(); mNavigationBarAlt = null; mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, null, null); } else if (mNotificationShade == win) { @@ -1437,49 +1432,6 @@ public class DisplayPolicy { return mForceShowSystemBars; } - /** - * Input handler used while nav bar is hidden. Captures any touch on the screen, - * to determine when the nav bar should be shown and prevent applications from - * receiving those touches. - */ - private final class HideNavInputEventReceiver extends InputEventReceiver { - HideNavInputEventReceiver(InputChannel inputChannel, Looper looper) { - super(inputChannel, looper); - } - - @Override - public void onInputEvent(InputEvent event) { - try { - if (event instanceof MotionEvent - && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { - final MotionEvent motionEvent = (MotionEvent) event; - if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { - // When the user taps down, we re-show the nav bar. - boolean changed = false; - synchronized (mLock) { - if (mInputConsumer == null) { - return; - } - showSystemBars(); - } - } - } - } finally { - finishInputEvent(event, false /* handled */); - } - } - - private void showSystemBars() { - final InsetsSourceProvider provider = mDisplayContent.getInsetsStateController() - .peekSourceProvider(ITYPE_NAVIGATION_BAR); - final InsetsControlTarget target = - provider != null ? provider.getControlTarget() : null; - if (target != null) { - target.showInsets(Type.systemBars(), false /* fromIme */); - } - } - } - private void simulateLayoutDecorWindow(WindowState win, DisplayFrames displayFrames, InsetsState insetsState, WindowFrames simulatedWindowFrames, SparseArray<Rect> contentFrames, Consumer<Rect> layout) { @@ -1529,48 +1481,10 @@ public class DisplayPolicy { displayFrames.onBeginLayout(mDisplayContent.getInsetsStateController().getRawInsetsState()); mSystemGestures.screenWidth = displayFrames.mUnrestricted.width(); mSystemGestures.screenHeight = displayFrames.mUnrestricted.height(); - - updateHideNavInputEventReceiver(); - layoutNavigationBar(displayFrames, uiMode, null /* simulatedContentFrame */); layoutStatusBar(displayFrames, null /* simulatedContentFrame */); } - void updateHideNavInputEventReceiver() { - final InsetsSourceProvider provider = mDisplayContent.getInsetsStateController() - .peekSourceProvider(ITYPE_NAVIGATION_BAR); - final InsetsControlTarget navControlTarget = - provider != null ? provider.getControlTarget() : null; - final WindowState navControllingWin = - navControlTarget instanceof WindowState ? (WindowState) navControlTarget : null; - final boolean navVisible = navControllingWin != null - ? navControllingWin.getRequestedVisibility(ITYPE_NAVIGATION_BAR) - : InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR); - final boolean showBarsByTouch = navControllingWin != null - && navControllingWin.mAttrs.insetsFlags.behavior == BEHAVIOR_SHOW_BARS_BY_TOUCH; - // When the navigation bar isn't visible, we put up a fake input window to catch all - // touch events. This way we can detect when the user presses anywhere to bring back the - // nav bar and ensure the application doesn't see the event. - if (navVisible || !showBarsByTouch) { - if (mInputConsumer != null) { - mInputConsumer.dismiss(); - mHandler.sendMessage( - mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer)); - mInputConsumer = null; - Slog.v(TAG, INPUT_CONSUMER_NAVIGATION + " dismissed."); - } - } else if (mInputConsumer == null && getStatusBar() != null && canHideNavigationBar()) { - mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer( - mHandler.getLooper(), - INPUT_CONSUMER_NAVIGATION, - HideNavInputEventReceiver::new); - Slog.v(TAG, INPUT_CONSUMER_NAVIGATION + " created."); - // As long as mInputConsumer is active, hover events are not dispatched to the app - // and the pointer icon is likely to become stale. Hide it to avoid confusion. - InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL); - } - } - private void layoutStatusBar(DisplayFrames displayFrames, Rect simulatedContentFrame) { // decide where the status bar goes ahead of time if (mStatusBar == null) { @@ -2195,6 +2109,13 @@ public class DisplayPolicy { - getNavigationBarFrameHeight(portraitRotation, uiMode); updateConfigurationAndScreenSizeDependentBehaviors(); + + final boolean shouldAttach = + res.getBoolean(R.bool.config_attachNavBarToAppDuringTransition); + if (mShouldAttachNavBarToAppDuringTransition != shouldAttach) { + mShouldAttachNavBarToAppDuringTransition = shouldAttach; + updateNavBarFadeController(); + } } void updateConfigurationAndScreenSizeDependentBehaviors() { @@ -2645,8 +2566,6 @@ public class DisplayPolicy { mTopFullscreenOpaqueOrDimmingWindowState, mDisplayContent.mInputMethodWindow, navColorWin) | opaqueAppearance; final int behavior = win.mAttrs.insetsFlags.behavior; - final boolean isImmersive = behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE - || behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; final boolean isFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR) || !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR); if (mLastDisableFlags == disableFlags @@ -2655,7 +2574,6 @@ public class DisplayPolicy { && mLastDockedAppearance == dockedAppearance && mLastBehavior == behavior && mLastFocusIsFullscreen == isFullscreen - && mLastFocusIsImmersive == isImmersive && mLastNonDockedStackBounds.equals(mNonDockedStackBounds) && mLastDockedStackBounds.equals(mDockedStackBounds)) { return false; @@ -2671,7 +2589,6 @@ public class DisplayPolicy { mLastDockedAppearance = dockedAppearance; mLastBehavior = behavior; mLastFocusIsFullscreen = isFullscreen; - mLastFocusIsImmersive = isImmersive; mLastNonDockedStackBounds.set(mNonDockedStackBounds); mLastDockedStackBounds.set(mDockedStackBounds); final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds); @@ -2688,9 +2605,8 @@ public class DisplayPolicy { if (statusBar != null) { final int displayId = getDisplayId(); statusBar.setDisableFlags(displayId, disableFlags, cause); - statusBar.onSystemBarAppearanceChanged(displayId, appearance, - appearanceRegions, isNavbarColorManagedByIme); - statusBar.topAppWindowChanged(displayId, isFullscreen, isImmersive); + statusBar.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions, + isNavbarColorManagedByIme, behavior, isFullscreen); } }); @@ -2922,11 +2838,8 @@ public class DisplayPolicy { if (win == null) { return false; } - final int behavior = win.mAttrs.insetsFlags.behavior; return getNavigationBar() != null && canHideNavigationBar() - && (behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE - || behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) && getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR) && win != getNotificationShade() && !win.isActivityTypeDream(); @@ -3177,4 +3090,26 @@ public class DisplayPolicy { return Rect.intersects(targetWindow.getFrame(), navBarWindow.getFrame()); } + + /** + * @return Whether we should attach navigation bar to the app during transition. + */ + boolean shouldAttachNavBarToAppDuringTransition() { + return mShouldAttachNavBarToAppDuringTransition && mNavigationBar != null; + } + + @Nullable NavBarFadeAnimationController getNavBarFadeAnimationController() { + return mNavBarFadeAnimationController; + } + + private void updateNavBarFadeController() { + if (shouldAttachNavBarToAppDuringTransition()) { + if (mNavBarFadeAnimationController == null) { + mNavBarFadeAnimationController = + new NavBarFadeAnimationController(mDisplayContent); + } + } else { + mNavBarFadeAnimationController = null; + } + } } diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index c4aaf7c8a935..df5d3ea3c150 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -955,10 +955,16 @@ public class DisplayRotation { keyguardDrawComplete, windowManagerDrawComplete); boolean disable = true; + + // If the orientation listener uses a wake sensor, keep the orientation listener on if the + // screen is on (regardless of wake state). This allows the AoD to rotate. + // // Note: We postpone the rotating of the screen until the keyguard as well as the // window manager have reported a draw complete or the keyguard is going away in dismiss // mode. - if (screenOnEarly && awake && ((keyguardDrawComplete && windowManagerDrawComplete))) { + if (screenOnEarly + && (awake || mOrientationListener.shouldStayEnabledWhileDreaming()) + && ((keyguardDrawComplete && windowManagerDrawComplete))) { if (needSensorRunning()) { disable = false; // Enable listener if not already enabled. @@ -974,7 +980,7 @@ public class DisplayRotation { } } // Check if sensors need to be disabled. - if (disable && mOrientationListener.mEnabled) { + if (disable) { mOrientationListener.disable(); } } diff --git a/services/core/java/com/android/server/wm/FadeAnimationController.java b/services/core/java/com/android/server/wm/FadeAnimationController.java new file mode 100644 index 000000000000..17d20ae4f92b --- /dev/null +++ b/services/core/java/com/android/server/wm/FadeAnimationController.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static com.android.server.wm.AnimationSpecProto.WINDOW; +import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION; + +import android.annotation.NonNull; +import android.content.Context; +import android.util.ArrayMap; +import android.util.proto.ProtoOutputStream; +import android.view.SurfaceControl; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.Transformation; + +import com.android.internal.R; + +import java.io.PrintWriter; + +/** + * An animation controller to fade-in/out for a window token. + */ +public class FadeAnimationController { + protected final Context mContext; + private final ArrayMap<WindowToken, Runnable> mDeferredFinishCallbacks = new ArrayMap<>(); + + public FadeAnimationController(DisplayContent displayContent) { + mContext = displayContent.mWmService.mContext; + } + + /** + * @return a fade-in Animation. + */ + public Animation getFadeInAnimation() { + return AnimationUtils.loadAnimation(mContext, R.anim.fade_in); + } + + /** + * @return a fade-out Animation. + */ + public Animation getFadeOutAnimation() { + return AnimationUtils.loadAnimation(mContext, R.anim.fade_out); + } + + /** + * Run the fade in/out animation for a window token. + * + * @param show true for fade-in, otherwise for fade-out. + * @param windowToken the window token to run the animation. + * @param animationType the animation type defined in SurfaceAnimator. + */ + public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) { + if (windowToken == null || windowToken.getParent() == null) { + return; + } + + final Animation animation = show ? getFadeInAnimation() : getFadeOutAnimation(); + if (animation == null) { + return; + } + + final LocalAnimationAdapter.AnimationSpec windowAnimationSpec = + createAnimationSpec(animation); + + final FadeAnimationAdapter animationAdapter = new FadeAnimationAdapter( + windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, windowToken); + + // We deferred the end of the animation when hiding the token, so we need to end it now that + // it's shown again. + final SurfaceAnimator.OnAnimationFinishedCallback finishedCallback = show ? (t, r) -> { + final Runnable runnable = mDeferredFinishCallbacks.remove(windowToken); + if (runnable != null) { + runnable.run(); + } + } : null; + windowToken.startAnimation(windowToken.getPendingTransaction(), animationAdapter, + show /* hidden */, animationType, finishedCallback); + } + + private LocalAnimationAdapter.AnimationSpec createAnimationSpec(@NonNull Animation animation) { + return new LocalAnimationAdapter.AnimationSpec() { + + final Transformation mTransformation = new Transformation(); + + @Override + public boolean getShowWallpaper() { + return true; + } + + @Override + public long getDuration() { + return animation.getDuration(); + } + + @Override + public void apply(SurfaceControl.Transaction t, SurfaceControl leash, + long currentPlayTime) { + mTransformation.clear(); + animation.getTransformation(currentPlayTime, mTransformation); + t.setAlpha(leash, mTransformation.getAlpha()); + } + + @Override + public void dump(PrintWriter pw, String prefix) { + pw.print(prefix); + pw.println(animation); + } + + @Override + public void dumpDebugInner(ProtoOutputStream proto) { + final long token = proto.start(WINDOW); + proto.write(ANIMATION, animation.toString()); + proto.end(token); + } + }; + } + + private class FadeAnimationAdapter extends LocalAnimationAdapter { + private final boolean mShow; + private final WindowToken mToken; + + FadeAnimationAdapter(AnimationSpec windowAnimationSpec, + SurfaceAnimationRunner surfaceAnimationRunner, boolean show, + WindowToken token) { + super(windowAnimationSpec, surfaceAnimationRunner); + mShow = show; + mToken = token; + } + + @Override + public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) { + // We defer the end of the hide animation to ensure the tokens stay hidden until + // we show them again. + if (!mShow) { + mDeferredFinishCallbacks.put(mToken, endDeferFinishCallback); + return true; + } + return false; + } + } +} diff --git a/services/core/java/com/android/server/wm/FixedRotationAnimationController.java b/services/core/java/com/android/server/wm/FixedRotationAnimationController.java index cc02e991c2ae..a1e3ac71a3b6 100644 --- a/services/core/java/com/android/server/wm/FixedRotationAnimationController.java +++ b/services/core/java/com/android/server/wm/FixedRotationAnimationController.java @@ -16,21 +16,8 @@ package com.android.server.wm; -import static com.android.server.wm.AnimationSpecProto.WINDOW; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM; -import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION; -import android.content.Context; -import android.util.ArrayMap; -import android.util.proto.ProtoOutputStream; -import android.view.SurfaceControl; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; -import android.view.animation.Transformation; - -import com.android.internal.R; - -import java.io.PrintWriter; import java.util.ArrayList; /** @@ -40,16 +27,14 @@ import java.util.ArrayList; * The system bars will be fade out when the fixed rotation transform starts and will be fade in * once all surfaces have been rotated. */ -public class FixedRotationAnimationController { +public class FixedRotationAnimationController extends FadeAnimationController { - private final Context mContext; private final WindowState mStatusBar; private final WindowState mNavigationBar; private final ArrayList<WindowToken> mAnimatedWindowToken = new ArrayList<>(2); - private final ArrayMap<WindowToken, Runnable> mDeferredFinishCallbacks = new ArrayMap<>(); public FixedRotationAnimationController(DisplayContent displayContent) { - mContext = displayContent.mWmService.mContext; + super(displayContent); final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); mStatusBar = displayPolicy.getStatusBar(); // Do not animate movable navigation bar (e.g. non-gesture mode). @@ -62,105 +47,25 @@ public class FixedRotationAnimationController { void show() { for (int i = mAnimatedWindowToken.size() - 1; i >= 0; i--) { final WindowToken windowToken = mAnimatedWindowToken.get(i); - fadeWindowToken(true /* show */, windowToken); + fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM); } } /** Applies hide animation on the window tokens which may be seamlessly rotated later. */ void hide() { if (mNavigationBar != null) { - fadeWindowToken(false /* show */, mNavigationBar.mToken); + fadeWindowToken(false /* show */, mNavigationBar.mToken, + ANIMATION_TYPE_FIXED_TRANSFORM); } if (mStatusBar != null) { - fadeWindowToken(false /* show */, mStatusBar.mToken); + fadeWindowToken(false /* show */, mStatusBar.mToken, + ANIMATION_TYPE_FIXED_TRANSFORM); } } - private void fadeWindowToken(boolean show, WindowToken windowToken) { - if (windowToken == null || windowToken.getParent() == null) { - return; - } - - final Animation animation = AnimationUtils.loadAnimation(mContext, - show ? R.anim.fade_in : R.anim.fade_out); - final LocalAnimationAdapter.AnimationSpec windowAnimationSpec = - createAnimationSpec(animation); - - final FixedRotationAnimationAdapter animationAdapter = new FixedRotationAnimationAdapter( - windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, windowToken); - - // We deferred the end of the animation when hiding the token, so we need to end it now that - // it's shown again. - final SurfaceAnimator.OnAnimationFinishedCallback finishedCallback = show ? (t, r) -> { - final Runnable runnable = mDeferredFinishCallbacks.remove(windowToken); - if (runnable != null) { - runnable.run(); - } - } : null; - windowToken.startAnimation(windowToken.getPendingTransaction(), animationAdapter, - show /* hidden */, ANIMATION_TYPE_FIXED_TRANSFORM, finishedCallback); + @Override + public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) { + super.fadeWindowToken(show, windowToken, animationType); mAnimatedWindowToken.add(windowToken); } - - private LocalAnimationAdapter.AnimationSpec createAnimationSpec(Animation animation) { - return new LocalAnimationAdapter.AnimationSpec() { - - final Transformation mTransformation = new Transformation(); - - @Override - public boolean getShowWallpaper() { - return true; - } - - @Override - public long getDuration() { - return animation.getDuration(); - } - - @Override - public void apply(SurfaceControl.Transaction t, SurfaceControl leash, - long currentPlayTime) { - mTransformation.clear(); - animation.getTransformation(currentPlayTime, mTransformation); - t.setAlpha(leash, mTransformation.getAlpha()); - } - - @Override - public void dump(PrintWriter pw, String prefix) { - pw.print(prefix); - pw.println(animation); - } - - @Override - public void dumpDebugInner(ProtoOutputStream proto) { - final long token = proto.start(WINDOW); - proto.write(ANIMATION, animation.toString()); - proto.end(token); - } - }; - } - - private class FixedRotationAnimationAdapter extends LocalAnimationAdapter { - private final boolean mShow; - private final WindowToken mToken; - - FixedRotationAnimationAdapter(AnimationSpec windowAnimationSpec, - SurfaceAnimationRunner surfaceAnimationRunner, boolean show, - WindowToken token) { - super(windowAnimationSpec, surfaceAnimationRunner); - mShow = show; - mToken = token; - } - - @Override - public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) { - // We defer the end of the hide animation to ensure the tokens stay hidden until - // we show them again. - if (!mShow) { - mDeferredFinishCallbacks.put(mToken, endDeferFinishCallback); - return true; - } - return false; - } - } } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 560547ed1a20..a20d924de058 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -19,7 +19,6 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.INVALID_DISPLAY; -import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION; import static android.view.WindowManager.INPUT_CONSUMER_PIP; import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER; @@ -53,7 +52,6 @@ import android.graphics.Region; import android.os.Handler; import android.os.IBinder; import android.os.Looper; -import android.os.Process; import android.os.Trace; import android.os.UserHandle; import android.util.ArrayMap; @@ -232,24 +230,6 @@ final class InputMonitor { } } - EventReceiverInputConsumer createInputConsumer(Looper looper, String name, - InputEventReceiver.Factory inputEventReceiverFactory) { - if (!name.contentEquals(INPUT_CONSUMER_NAVIGATION)) { - throw new IllegalArgumentException("Illegal input consumer : " + name - + ", display: " + mDisplayId); - } - - if (mInputConsumers.containsKey(name)) { - throw new IllegalStateException("Existing input consumer found with name: " + name - + ", display: " + mDisplayId); - } - final EventReceiverInputConsumer consumer = new EventReceiverInputConsumer(mService, - this, looper, name, inputEventReceiverFactory, Process.myPid(), - UserHandle.SYSTEM, mDisplayId); - addInputConsumer(name, consumer); - return consumer; - } - void createInputConsumer(IBinder token, String name, InputChannel inputChannel, int clientPid, UserHandle clientUser) { if (mInputConsumers.containsKey(name)) { @@ -472,12 +452,10 @@ final class InputMonitor { } private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> { - InputConsumerImpl mNavInputConsumer; InputConsumerImpl mPipInputConsumer; InputConsumerImpl mWallpaperInputConsumer; InputConsumerImpl mRecentsAnimationInputConsumer; - private boolean mAddNavInputConsumerHandle; private boolean mAddPipInputConsumerHandle; private boolean mAddWallpaperInputConsumerHandle; private boolean mAddRecentsAnimationInputConsumerHandle; @@ -487,12 +465,10 @@ final class InputMonitor { private void updateInputWindows(boolean inDrag) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows"); - mNavInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION); mPipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP); mWallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER); mRecentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION); - mAddNavInputConsumerHandle = mNavInputConsumer != null; mAddPipInputConsumerHandle = mPipInputConsumer != null; mAddWallpaperInputConsumerHandle = mWallpaperInputConsumer != null; mAddRecentsAnimationInputConsumerHandle = mRecentsAnimationInputConsumer != null; @@ -557,12 +533,6 @@ final class InputMonitor { } } - if (mAddNavInputConsumerHandle) { - // We set the layer to z=MAX-1 so that it's always on top. - mNavInputConsumer.show(mInputTransaction, Integer.MAX_VALUE - 1); - mAddNavInputConsumerHandle = false; - } - if (mAddWallpaperInputConsumerHandle) { if (w.mAttrs.type == TYPE_WALLPAPER && w.isVisible()) { // Add the wallpaper input consumer above the first visible wallpaper. diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index ee150c31184c..94a7ebd37760 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -142,7 +142,6 @@ class InsetsPolicy { getFakeControlTarget(focusedWin, navControlTarget)); mStatusBar.updateVisibility(statusControlTarget, ITYPE_STATUS_BAR); mNavBar.updateVisibility(navControlTarget, ITYPE_NAVIGATION_BAR); - mPolicy.updateHideNavInputEventReceiver(); } boolean isHidden(@InternalInsetsType int type) { diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 40e7a8e9311b..0dfa2d8136c5 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -152,6 +152,7 @@ class InsetsSourceProvider { // animate-out as new one animates-in. mWin.cancelAnimation(); mWin.mPendingPositionChanged = null; + mWin.mProvidedInsetsSources.remove(mSource.getType()); } ProtoLog.d(WM_DEBUG_IME, "InsetsSource setWin %s", win); mWin = win; @@ -161,11 +162,14 @@ class InsetsSourceProvider { setServerVisible(false); mSource.setFrame(new Rect()); mSource.setVisibleFrame(null); - } else if (mControllable) { - mWin.setControllableInsetProvider(this); - if (mPendingControlTarget != null) { - updateControlForTarget(mPendingControlTarget, true /* force */); - mPendingControlTarget = null; + } else { + mWin.mProvidedInsetsSources.put(mSource.getType(), mSource); + if (mControllable) { + mWin.setControllableInsetProvider(this); + if (mPendingControlTarget != null) { + updateControlForTarget(mPendingControlTarget, true /* force */); + mPendingControlTarget = null; + } } } } diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 752d6b4fc908..a1461b2956fd 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -104,6 +104,8 @@ class InsetsStateController { * visible to the target. e.g., the source which represents the target window itself, and the * IME source when the target is above IME. We also need to exclude certain types of insets * source for client within specific windowing modes. + * This is to get the insets for a window layout on the screen. If the window is not there, use + * the {@link #getInsetsForWindowMetrics} to get insets instead. * * @param target The window associate with the perspective. * @return The state stripped of the necessary information. @@ -117,7 +119,7 @@ class InsetsStateController { final @InternalInsetsType int type = provider != null ? provider.getSource().getType() : ITYPE_INVALID; return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(), - isAboveIme(target)); + target.mAboveInsetsState); } InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) { @@ -132,19 +134,7 @@ class InsetsStateController { final @WindowingMode int windowingMode = token != null ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED; final boolean alwaysOnTop = token != null && token.isAlwaysOnTop(); - return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token)); - } - - private boolean isAboveIme(WindowContainer target) { - final WindowState imeWindow = mDisplayContent.mInputMethodWindow; - if (target == null || imeWindow == null) { - return false; - } - if (target instanceof WindowState) { - final WindowState win = (WindowState) target; - return win.needsRelativeLayeringToIme() || !win.mBehindIme; - } - return false; + return getInsetsForTarget(type, windowingMode, alwaysOnTop, mState); } private static @InternalInsetsType @@ -180,11 +170,12 @@ class InsetsStateController { * @see #getInsetsForWindowMetrics */ private InsetsState getInsetsForTarget(@InternalInsetsType int type, - @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme) { - InsetsState state = mState; + @WindowingMode int windowingMode, boolean isAlwaysOnTop, InsetsState state) { + boolean stateCopied = false; if (type != ITYPE_INVALID) { state = new InsetsState(state); + stateCopied = true; state.removeSource(type); // Navigation bar doesn't get influenced by anything else @@ -219,23 +210,15 @@ class InsetsStateController { if (WindowConfiguration.isFloating(windowingMode) || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) { - state = new InsetsState(state); + if (!stateCopied) { + state = new InsetsState(state); + stateCopied = true; + } state.removeSource(ITYPE_STATUS_BAR); state.removeSource(ITYPE_NAVIGATION_BAR); state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR); } - if (aboveIme) { - InsetsSource imeSource = state.peekSource(ITYPE_IME); - if (imeSource != null && imeSource.isVisible()) { - imeSource = new InsetsSource(imeSource); - imeSource.setVisible(false); - imeSource.setFrame(0, 0, 0, 0); - state = new InsetsState(state); - state.addSource(imeSource); - } - } - return state; } diff --git a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java new file mode 100644 index 000000000000..30861eb3a32d --- /dev/null +++ b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; + +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.Interpolator; +import android.view.animation.PathInterpolator; + +/** + * Controller to fade in and out navigation bar during app transition when + * config_attachNavBarToAppDuringTransition is true. + */ +public class NavBarFadeAnimationController extends FadeAnimationController{ + private static final int FADE_IN_DURATION = 266; + private static final int FADE_OUT_DURATION = 133; + private static final Interpolator FADE_IN_INTERPOLATOR = + new PathInterpolator(0f, 0f, 0f, 1f); + private static final Interpolator FADE_OUT_INTERPOLATOR = + new PathInterpolator(0.2f, 0f, 1f, 1f); + + private final WindowState mNavigationBar; + private Animation mFadeInAnimation; + private Animation mFadeOutAnimation; + + public NavBarFadeAnimationController(DisplayContent displayContent) { + super(displayContent); + mNavigationBar = displayContent.getDisplayPolicy().getNavigationBar(); + mFadeInAnimation = new AlphaAnimation(0f, 1f); + mFadeInAnimation.setDuration(FADE_IN_DURATION); + mFadeInAnimation.setInterpolator(FADE_IN_INTERPOLATOR); + + mFadeOutAnimation = new AlphaAnimation(1f, 0f); + mFadeOutAnimation.setDuration(FADE_OUT_DURATION); + mFadeOutAnimation.setInterpolator(FADE_OUT_INTERPOLATOR); + } + + @Override + public Animation getFadeInAnimation() { + return mFadeInAnimation; + } + + @Override + public Animation getFadeOutAnimation() { + return mFadeOutAnimation; + } + + /** + * Run the fade-in/out animation for the navigation bar. + * + * @param show true for fade-in, otherwise for fade-out. + */ + public void fadeWindowToken(boolean show) { + fadeWindowToken(show, mNavigationBar.mToken, ANIMATION_TYPE_APP_TRANSITION); + } +} diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 388577cbfade..00b6cebbe32d 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -33,7 +33,6 @@ import static com.android.server.wm.WindowManagerInternal.AppTransitionListener; import android.annotation.IntDef; import android.annotation.NonNull; -import android.window.TaskSnapshot; import android.app.WindowConfiguration; import android.graphics.Point; import android.graphics.Rect; @@ -54,6 +53,7 @@ import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.WindowInsets.Type; +import android.window.TaskSnapshot; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.SoftInputShowHideReason; @@ -147,6 +147,9 @@ public class RecentsAnimationController implements DeathRecipient { // Whether to take a screenshot when handling a deferred cancel private boolean mCancelDeferredWithScreenshot; + @VisibleForTesting + boolean mShouldAttachNavBarToAppDuringTransition; + /** * Animates the screenshot of task that used to be controlled by RecentsAnimation. * @see {@link #setCancelOnNextTransitionStart} @@ -390,6 +393,8 @@ public class RecentsAnimationController implements DeathRecipient { mDisplayId = displayId; mStatusBar = LocalServices.getService(StatusBarManagerInternal.class); mDisplayContent = service.mRoot.getDisplayContent(displayId); + mShouldAttachNavBarToAppDuringTransition = + mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition(); } /** @@ -439,6 +444,10 @@ public class RecentsAnimationController implements DeathRecipient { return; } + if (mShouldAttachNavBarToAppDuringTransition) { + attachNavBarToApp(); + } + // Adjust the wallpaper visibility for the showing target activity ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "setHomeApp(%s)", targetActivity.getName()); @@ -572,6 +581,63 @@ public class RecentsAnimationController implements DeathRecipient { } } + @VisibleForTesting + WindowToken getNavigationBarWindowToken() { + WindowState navBar = mDisplayContent.getDisplayPolicy().getNavigationBar(); + if (navBar != null) { + return navBar.mToken; + } + return null; + } + + private void attachNavBarToApp() { + ActivityRecord topActivity = null; + for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { + final TaskAnimationAdapter adapter = mPendingAnimations.get(i); + final Task task = adapter.mTask; + if (!task.isHomeOrRecentsRootTask()) { + topActivity = task.getTopVisibleActivity(); + break; + } + } + final WindowToken navToken = getNavigationBarWindowToken(); + if (topActivity == null || navToken == null) { + return; + } + + final SurfaceControl.Transaction t = navToken.getPendingTransaction(); + final SurfaceControl navSurfaceControl = navToken.getSurfaceControl(); + t.reparent(navSurfaceControl, topActivity.getSurfaceControl()); + t.show(navSurfaceControl); + + final WindowContainer imeContainer = mDisplayContent.getImeContainer(); + if (imeContainer.isVisible()) { + t.setRelativeLayer(navSurfaceControl, imeContainer.getSurfaceControl(), 1); + } else { + // Place the nav bar on top of anything else in the top activity. + t.setLayer(navSurfaceControl, Integer.MAX_VALUE); + } + } + + private void restoreNavBarFromApp(boolean animate) { + // Reparent the SurfaceControl of nav bar token back. + final WindowToken navToken = getNavigationBarWindowToken(); + final SurfaceControl.Transaction t = mDisplayContent.getPendingTransaction(); + if (navToken != null) { + final WindowContainer parent = navToken.getParent(); + t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl()); + } + + if (animate) { + // Run fade-in animation to show navigation bar back to bottom of the display. + final NavBarFadeAnimationController controller = + mDisplayContent.getDisplayPolicy().getNavBarFadeAnimationController(); + if (controller != null) { + controller.fadeWindowToken(true); + } + } + } + void addTaskToTargets(Task task, OnAnimationFinishedCallback finishedCallback) { if (mRunner != null) { // No need to send task appeared when the task target already exists, or when the @@ -790,6 +856,10 @@ public class RecentsAnimationController implements DeathRecipient { removeWallpaperAnimation(wallpaperAdapter); } + if (mShouldAttachNavBarToAppDuringTransition) { + restoreNavBarFromApp(reorderMode == REORDER_MOVE_TO_TOP); + } + // Clear any pending failsafe runnables mService.mH.removeCallbacks(mFailsafeRunnable); mDisplayContent.mAppTransition.unregisterListener(mAppTransitionListener); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 23acbf75429c..d8b1e188e34a 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -862,6 +862,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces"); } } + + // Send any pending task-info changes that were queued-up during a layout deferment + mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); mWmService.mAnimator.executeAfterPrepareSurfacesRunnables(); checkAppTransitionReady(surfacePlacer); @@ -1014,9 +1017,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mWmService.scheduleAnimationLocked(); - // Send any pending task-info changes that were queued-up during a layout deferment - mWmService.mAtmService.mTaskOrganizerController.dispatchPendingTaskInfoChanges(); - if (DEBUG_WINDOW_TRACE) Slog.e(TAG, "performSurfacePlacementInner exit"); } @@ -1545,6 +1545,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> homeIntent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, true); mWindowManager.cancelRecentsAnimation(REORDER_KEEP_IN_PLACE, "startHomeActivity"); } + homeIntent.putExtra(WindowManagerPolicy.EXTRA_START_REASON, reason); + // Update the reason for ANR debugging to verify if the user activity is the one that // actually launched. final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId( diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index 1a27b1bc4a6e..533c82e599c9 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -206,12 +206,17 @@ class ScreenRotationAnimation { .setCallsite("ScreenRotationAnimation") .build(); + String name = "RotationLayer"; mScreenshotLayer = displayContent.makeOverlay() - .setName("RotationLayer") + .setName(name) .setBufferSize(mWidth, mHeight) .setSecure(isSecure) .setCallsite("ScreenRotationAnimation") .build(); + // This is the way to tell the input system to exclude this surface from occlusion + // detection since we don't have a window for it. We do this because this window is + // generated by the system as well as its content. + InputMonitor.setTrustedOverlayInputInfo(mScreenshotLayer, t, displayId, name); mEnterBlackFrameLayer = displayContent.makeOverlay() .setName("EnterBlackFrameLayer") diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 637240c06de2..855c8f562dda 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -424,6 +424,9 @@ class Task extends WindowContainer<WindowContainer> { /** If original intent did not allow relinquishing task identity, save that information */ private boolean mNeverRelinquishIdentity = true; + /** Avoid reentrant of {@link #removeImmediately(String)}. */ + private boolean mRemoving; + // Used in the unique case where we are clearing the task in order to reuse it. In that case we // do not want to delete the stack when the task goes empty. private boolean mReuseTask = false; @@ -2825,9 +2828,7 @@ class Task extends WindowContainer<WindowContainer> { getResolvedOverrideConfiguration().windowConfiguration.getBounds(); if (windowingMode == WINDOWING_MODE_FULLSCREEN) { - computeFullscreenBounds(outOverrideBounds, null /* refActivity */, - newParentConfig.windowConfiguration.getBounds(), - newParentConfig.orientation); + computeFullscreenBounds(outOverrideBounds, newParentConfig); // The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if // the parent or display is smaller than the size, the content may be cropped. return; @@ -2867,19 +2868,19 @@ class Task extends WindowContainer<WindowContainer> { * {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN} when the parent doesn't handle the * orientation change and the requested orientation is different from the parent. */ - void computeFullscreenBounds(@NonNull Rect outBounds, @Nullable ActivityRecord refActivity, - @NonNull Rect parentBounds, int parentOrientation) { + void computeFullscreenBounds(@NonNull Rect outBounds, @NonNull Configuration newParentConfig) { // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent". outBounds.setEmpty(); if (handlesOrientationChangeFromDescendant()) { + // No need to letterbox at task level. Display will handle fixed-orientation requests. return; } - if (refActivity == null) { - // Use the top activity as the reference of orientation. Don't include overlays because - // it is usually not the actual content or just temporarily shown. - // E.g. ForcedResizableInfoActivity. - refActivity = getTopNonFinishingActivity(false /* includeOverlays */); - } + + final int parentOrientation = newParentConfig.orientation; + // Use the top activity as the reference of orientation. Don't include overlays because + // it is usually not the actual content or just temporarily shown. + // E.g. ForcedResizableInfoActivity. + final ActivityRecord refActivity = getTopNonFinishingActivity(false /* includeOverlays */); // If the task or the reference activity requires a different orientation (either by // override or activityInfo), make it fit the available bounds by scaling down its bounds. @@ -2891,11 +2892,17 @@ class Task extends WindowContainer<WindowContainer> { return; } - if (refActivity != null && refActivity.hasCompatDisplayInsets()) { + final ActivityRecord.CompatDisplayInsets compatDisplayInsets = + refActivity == null ? null : refActivity.getCompatDisplayInsets(); + if (compatDisplayInsets != null && !compatDisplayInsets.mIsTaskLetterboxed) { // App prefers to keep its original size. + // If the size compat is from previous task letterboxing, we may want to have task + // letterbox again, otherwise it will show the size compat restart button even if the + // restart bounds will be the same. return; } + final Rect parentBounds = newParentConfig.windowConfiguration.getBounds(); final int parentWidth = parentBounds.width(); final int parentHeight = parentBounds.height(); float aspect = Math.max(parentWidth, parentHeight) @@ -2930,6 +2937,18 @@ class Task extends WindowContainer<WindowContainer> { final int left = parentBounds.centerX() - width / 2; outBounds.set(left, parentBounds.top, left + width, parentBounds.bottom); } + + if (compatDisplayInsets != null) { + compatDisplayInsets.getBoundsByRotation( + mTmpBounds, newParentConfig.windowConfiguration.getRotation()); + if (outBounds.width() != mTmpBounds.width() + || outBounds.height() != mTmpBounds.height()) { + // The app shouldn't be resized, we only do task letterboxing if the compat bounds + // is also from the same task letterbox. Otherwise, clear the task bounds to show + // app in size compat mode. + outBounds.setEmpty(); + } + } } Rect updateOverrideConfigurationFromLaunchBounds() { @@ -3223,12 +3242,18 @@ class Task extends WindowContainer<WindowContainer> { void removeImmediately(String reason) { if (DEBUG_ROOT_TASK) Slog.i(TAG, "removeTask:" + reason + " removing taskId=" + mTaskId); + if (mRemoving) { + return; + } + mRemoving = true; + EventLogTags.writeWmTaskRemoved(mTaskId, reason); // If applicable let the TaskOrganizer know the Task is vanishing. setTaskOrganizer(null); super.removeImmediately(); + mRemoving = false; } // TODO: Consolidate this with Task.reparent() @@ -3304,6 +3329,14 @@ class Task extends WindowContainer<WindowContainer> { return false; } + @Override + boolean handlesOrientationChangeFromDescendant() { + return super.handlesOrientationChangeFromDescendant() + // Display won't rotate for the orientation request if the TaskDisplayArea can't + // specify orientation. + && getDisplayArea().canSpecifyOrientation(); + } + void resize(boolean relayout, boolean forced) { if (setBounds(getRequestedOverrideBounds(), forced) != BOUNDS_CHANGE_NONE && relayout) { getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); @@ -4993,7 +5026,6 @@ class Task extends WindowContainer<WindowContainer> { } } else { // No longer managed by any organizer. - mTaskAppearedSent = false; setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, false /* set */); if (mCreatedByOrganizer) { removeImmediately("setTaskOrganizer"); @@ -5019,7 +5051,7 @@ class Task extends WindowContainer<WindowContainer> { */ boolean updateTaskOrganizerState(boolean forceUpdate, boolean skipTaskAppeared) { if (getSurfaceControl() == null) { - // Can't call onTaskAppeared without a surfacecontrol, so defer this until after one + // Can't call onTaskAppeared without a surfacecontrol, so defer this until next one // is created. return false; } @@ -6036,8 +6068,6 @@ class Task extends WindowContainer<WindowContainer> { // If the top activity is the resumed one, nothing to do. if (mResumedActivity == next && next.isState(RESUMED) && taskDisplayArea.allResumedActivitiesComplete()) { - // The activity may be waiting for stop, but that is no longer appropriate for it. - mTaskSupervisor.mStoppingActivities.remove(next); // Make sure we have executed any pending transitions, since there // should be nothing left to do at this point. executeAppTransition(options); @@ -7167,6 +7197,7 @@ class Task extends WindowContainer<WindowContainer> { + " mode=" + windowingModeToString(getWindowingMode())); pw.println(" isSleeping=" + shouldSleepActivities()); pw.println(" mBounds=" + getRequestedOverrideBounds()); + pw.println(" mCreatedByOrganizer=" + mCreatedByOrganizer); }; boolean printed = false; @@ -7672,7 +7703,7 @@ class Task extends WindowContainer<WindowContainer> { void dispatchTaskInfoChangedIfNeeded(boolean force) { if (isOrganized()) { - mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, force); + mAtmService.mTaskOrganizerController.onTaskInfoChanged(this, force); } } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 832fc688d6eb..0136c010429b 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -19,7 +19,6 @@ package com.android.server.wm; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; @@ -60,6 +59,7 @@ import com.android.internal.util.function.pooled.PooledPredicate; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -1848,9 +1848,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { // Keep the order from bottom to top. int numRootTasks = mChildren.size(); - final boolean splitScreenActivated = toDisplayArea.isSplitScreenModeActivated(); - final Task splitScreenRoot = splitScreenActivated ? toDisplayArea - .getTopRootTaskInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) : null; for (int i = 0; i < numRootTasks; i++) { final WindowContainer child = mChildren.get(i); if (child.asTaskDisplayArea() != null) { @@ -1866,10 +1863,12 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { || task.mCreatedByOrganizer) { task.finishAllActivitiesImmediately(); } else { - // Reparent the root task to the root task of secondary-split-screen or display - // area. - task.reparent(task.supportsSplitScreenWindowingMode() && splitScreenRoot != null - ? splitScreenRoot : toDisplayArea, POSITION_TOP); + // Reparent task to corresponding launch root or display area. + final WindowContainer launchRoot = task.supportsSplitScreenWindowingMode() + ? toDisplayArea.getLaunchRootTask( + task.getWindowingMode(), task.getActivityType()) + : null; + task.reparent(launchRoot == null ? toDisplayArea : launchRoot, POSITION_TOP); // Set the windowing mode to undefined by default to let the root task inherited the // windowing mode. @@ -1881,14 +1880,19 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { i -= numRootTasks - mChildren.size(); numRootTasks = mChildren.size(); } - if (lastReparentedRootTask != null && splitScreenActivated) { - if (!lastReparentedRootTask.supportsSplitScreenWindowingMode()) { + + if (lastReparentedRootTask != null) { + if (toDisplayArea.isSplitScreenModeActivated() + && !lastReparentedRootTask.supportsSplitScreenWindowingMode()) { + // Dismiss split screen if the last reparented root task doesn't support split mode. mAtmService.getTaskChangeNotificationController() .notifyActivityDismissingDockedStack(); toDisplayArea.onSplitScreenModeDismissed(lastReparentedRootTask); - } else if (splitScreenRoot != null) { - // update focus - splitScreenRoot.moveToFront("display-removed"); + } else if (!lastReparentedRootTask.isRootTask()) { + // Update focus when the last reparented root task is not a root task anymore. + // (For example, if it has been reparented to a split screen root task, move the + // focus to the split root task) + lastReparentedRootTask.getRootTask().moveToFront("display-removed"); } } @@ -1898,7 +1902,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } /** Whether this task display area can request orientation. */ - @VisibleForTesting boolean canSpecifyOrientation() { // Only allow to specify orientation if this TDA is not set to ignore orientation request, // and it is the last focused one on this logical display that can request orientation @@ -1937,8 +1940,8 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) { final LaunchRootTaskDef def = mLaunchRootTasks.get(i); pw.println(triplePrefix - + def.activityTypes + " " - + def.windowingModes + " " + + Arrays.toString(def.activityTypes) + " " + + Arrays.toString(def.windowingModes) + " " + " task=" + def.task); } } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 65247d0b9937..b3e010872d3b 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -151,27 +151,23 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task appeared taskId=%d", task.mTaskId); final boolean visible = task.isVisible(); final RunningTaskInfo taskInfo = task.getTaskInfo(); - mDeferTaskOrgCallbacksConsumer.accept(() -> { - try { - mTaskOrganizer.onTaskAppeared(taskInfo, prepareLeash(task, visible, - "TaskOrganizerController.onTaskAppeared")); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending onTaskAppeared callback", e); - } - }); + try { + mTaskOrganizer.onTaskAppeared(taskInfo, prepareLeash(task, visible, + "TaskOrganizerController.onTaskAppeared")); + } catch (RemoteException e) { + Slog.e(TAG, "Exception sending onTaskAppeared callback", e); + } } void onTaskVanished(Task task) { ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task vanished taskId=%d", task.mTaskId); final RunningTaskInfo taskInfo = task.getTaskInfo(); - mDeferTaskOrgCallbacksConsumer.accept(() -> { - try { - mTaskOrganizer.onTaskVanished(taskInfo); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending onTaskVanished callback", e); - } - }); + try { + mTaskOrganizer.onTaskVanished(taskInfo); + } catch (RemoteException e) { + Slog.e(TAG, "Exception sending onTaskVanished callback", e); + } } void onTaskInfoChanged(Task task, ActivityManager.RunningTaskInfo taskInfo) { @@ -180,20 +176,18 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return; } ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task info changed taskId=%d", task.mTaskId); - mDeferTaskOrgCallbacksConsumer.accept(() -> { - if (!task.isOrganized()) { - // This is safe to ignore if the task is no longer organized - return; - } - try { - // Purposely notify of task info change immediately instead of deferring (like - // appear and vanish) to allow info changes (such as new PIP params) to flow - // without waiting. - mTaskOrganizer.onTaskInfoChanged(taskInfo); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending onTaskInfoChanged callback", e); - } - }); + if (!task.isOrganized()) { + // This is safe to ignore if the task is no longer organized + return; + } + try { + // Purposely notify of task info change immediately instead of deferring (like + // appear and vanish) to allow info changes (such as new PIP params) to flow + // without waiting. + mTaskOrganizer.onTaskInfoChanged(taskInfo); + } catch (RemoteException e) { + Slog.e(TAG, "Exception sending onTaskInfoChanged callback", e); + } } void onBackPressedOnTaskRoot(Task task) { @@ -203,17 +197,15 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { // Skip if the task has not yet received taskAppeared(). return; } - mDeferTaskOrgCallbacksConsumer.accept(() -> { - if (!task.isOrganized()) { - // This is safe to ignore if the task is no longer organized - return; - } - try { - mTaskOrganizer.onBackPressedOnTaskRoot(task.getTaskInfo()); - } catch (Exception e) { - Slog.e(TAG, "Exception sending onBackPressedOnTaskRoot callback", e); - } - }); + if (!task.isOrganized()) { + // This is safe to ignore if the task is no longer organized + return; + } + try { + mTaskOrganizer.onBackPressedOnTaskRoot(task.getTaskInfo()); + } catch (Exception e) { + Slog.e(TAG, "Exception sending onBackPressedOnTaskRoot callback", e); + } } } @@ -258,28 +250,34 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return mOrganizer.prepareLeash(t, t.isVisible(), reason); } - void addTask(Task t) { - if (t.mTaskAppearedSent) return; + private boolean addTask(Task t) { + if (t.mTaskAppearedSent) { + return false; + } if (!mOrganizedTasks.contains(t)) { mOrganizedTasks.add(t); } + if (t.taskAppearedReady()) { t.mTaskAppearedSent = true; - mOrganizer.onTaskAppeared(t); + return true; } + return false; } - void removeTask(Task t) { + private boolean removeTask(Task t) { + mOrganizedTasks.remove(t); + mInterceptBackPressedOnRootTasks.remove(t.mTaskId); + if (t.mTaskAppearedSent) { if (t.getSurfaceControl() != null) { t.migrateToNewSurfaceControl(); } t.mTaskAppearedSent = false; - mOrganizer.onTaskVanished(t); + return true; } - mOrganizedTasks.remove(t); - mInterceptBackPressedOnRootTasks.remove(t.mTaskId); + return false; } void dispose() { @@ -290,8 +288,14 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { // possible. while (!mOrganizedTasks.isEmpty()) { final Task t = mOrganizedTasks.get(0); - if (!t.updateTaskOrganizerState(true /* forceUpdate */)) { - removeTask(t); + t.updateTaskOrganizerState(true /* forceUpdate */); + if (mOrganizedTasks.contains(t)) { + // updateTaskOrganizerState should remove the task from the list, but still + // check it again to avoid while-loop isn't terminate. + if (removeTask(t)) { + TaskOrganizerController.this.onTaskVanishedInternal( + mOrganizer.mTaskOrganizer, t); + } } } @@ -305,6 +309,33 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } + static class PendingTaskEvent { + static final int EVENT_APPEARED = 0; + static final int EVENT_VANISHED = 1; + static final int EVENT_INFO_CHANGED = 2; + static final int EVENT_ROOT_BACK_PRESSED = 3; + + final int mEventType; + final Task mTask; + final ITaskOrganizer mTaskOrg; + boolean mForce; + + PendingTaskEvent(Task task, int event) { + this(task, task.mTaskOrganizer, event); + } + + PendingTaskEvent(Task task, ITaskOrganizer taskOrg, int eventType) { + mTask = task; + mTaskOrg = taskOrg; + mEventType = eventType; + } + + boolean isLifecycleEvent() { + return mEventType == EVENT_APPEARED || mEventType == EVENT_VANISHED + || mEventType == EVENT_INFO_CHANGED; + } + } + private final ActivityTaskManagerService mService; private final WindowManagerGlobalLock mGlobalLock; @@ -312,7 +343,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { private final LinkedList<ITaskOrganizer> mTaskOrganizers = new LinkedList<>(); private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>(); private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>(); - private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>(); + // Pending task events due to layout deferred. + private final ArrayList<PendingTaskEvent> mPendingTaskEvents = new ArrayList<>(); // Set of organized tasks (by taskId) that dispatch back pressed to their organizers private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet(); @@ -337,6 +369,12 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { public void setDeferTaskOrgCallbacksConsumer(Consumer<Runnable> consumer) { mDeferTaskOrgCallbacksConsumer = consumer; } + + @VisibleForTesting + ArrayList<PendingTaskEvent> getPendingEventList() { + return mPendingTaskEvents; + } + /** * Register a TaskOrganizer to manage tasks as they enter the a supported windowing mode. */ @@ -442,14 +480,38 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { void onTaskAppeared(ITaskOrganizer organizer, Task task) { final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); - state.addTask(task); + if (state != null && state.addTask(task)) { + PendingTaskEvent pending = getPendingTaskEvent(task, PendingTaskEvent.EVENT_APPEARED); + if (pending == null) { + pending = new PendingTaskEvent(task, PendingTaskEvent.EVENT_APPEARED); + mPendingTaskEvents.add(pending); + } + } } void onTaskVanished(ITaskOrganizer organizer, Task task) { final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); - if (state != null) { - state.removeTask(task); + if (state != null && state.removeTask(task)) { + onTaskVanishedInternal(organizer, task); + } + } + + private void onTaskVanishedInternal(ITaskOrganizer organizer, Task task) { + for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) { + PendingTaskEvent entry = mPendingTaskEvents.get(i); + if (task.mTaskId == entry.mTask.mTaskId) { + // This task is vanished so remove all pending event of it. + mPendingTaskEvents.remove(i); + if (entry.mEventType == PendingTaskEvent.EVENT_APPEARED) { + // If task appeared callback still pend, ignore this callback too. + return; + } + } } + + PendingTaskEvent pending = + new PendingTaskEvent(task, organizer, PendingTaskEvent.EVENT_VANISHED); + mPendingTaskEvents.add(pending); } @Override @@ -518,30 +580,76 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } - void dispatchPendingTaskInfoChanges() { - if (mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) { + void dispatchPendingEvents() { + if (mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred() + || mPendingTaskEvents.isEmpty()) { return; } - for (int i = 0, n = mPendingTaskInfoChanges.size(); i < n; ++i) { - dispatchTaskInfoChanged(mPendingTaskInfoChanges.get(i), false /* force */); + + for (int i = 0, n = mPendingTaskEvents.size(); i < n; i++) { + PendingTaskEvent event = mPendingTaskEvents.get(i); + final Task task = event.mTask; + final TaskOrganizerState state; + switch (event.mEventType) { + case PendingTaskEvent.EVENT_APPEARED: + state = mTaskOrganizerStates.get(event.mTaskOrg.asBinder()); + if (state != null && task.taskAppearedReady()) { + state.mOrganizer.onTaskAppeared(task); + } + break; + case PendingTaskEvent.EVENT_VANISHED: + state = mTaskOrganizerStates.get(event.mTaskOrg.asBinder()); + if (state != null) { + state.mOrganizer.onTaskVanished(task); + } + break; + case PendingTaskEvent.EVENT_INFO_CHANGED: + dispatchTaskInfoChanged(event.mTask, event.mForce); + break; + case PendingTaskEvent.EVENT_ROOT_BACK_PRESSED: + state = mTaskOrganizerStates.get(event.mTaskOrg.asBinder()); + if (state != null) { + state.mOrganizer.onBackPressedOnTaskRoot(task); + } + break; + } } - mPendingTaskInfoChanges.clear(); + mPendingTaskEvents.clear(); } - void dispatchTaskInfoChanged(Task task, boolean force) { - if (!force && mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) { - // Defer task info reporting while layout is deferred. This is because layout defer - // blocks tend to do lots of re-ordering which can mess up animations in receivers. - mPendingTaskInfoChanges.remove(task); - mPendingTaskInfoChanges.add(task); + void onTaskInfoChanged(Task task, boolean force) { + if (!task.mTaskAppearedSent) { + // Skip if task still not appeared. return; } + + // Defer task info reporting while layout is deferred. This is because layout defer + // blocks tend to do lots of re-ordering which can mess up animations in receivers. + PendingTaskEvent pending = getPendingLifecycleTaskEvent(task); + if (pending == null) { + pending = new PendingTaskEvent(task, PendingTaskEvent.EVENT_INFO_CHANGED); + } else { + if (pending.mEventType != PendingTaskEvent.EVENT_INFO_CHANGED) { + // If queued event is appeared, it means task still not appeared so ignore + // this info changed. If queued event is vanished, it means task should + // will vanished early so do not need this info changed. + return; + } + // Remove and add for re-ordering. + mPendingTaskEvents.remove(pending); + } + pending.mForce = force; + mPendingTaskEvents.add(pending); + } + + private void dispatchTaskInfoChanged(Task task, boolean force) { RunningTaskInfo lastInfo = mLastSentTaskInfos.get(task); if (mTmpTaskInfo == null) { mTmpTaskInfo = new RunningTaskInfo(); } mTmpTaskInfo.configuration.unset(); task.fillTaskInfo(mTmpTaskInfo); + boolean changed = !mTmpTaskInfo.equalsForTaskOrganizer(lastInfo); if (!changed) { int cfgChanges = mTmpTaskInfo.configuration.diff(lastInfo.configuration); @@ -705,11 +813,48 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return false; } - final TaskOrganizerState state = mTaskOrganizerStates.get(task.mTaskOrganizer.asBinder()); - state.mOrganizer.onBackPressedOnTaskRoot(task); + PendingTaskEvent pendingVanished = + getPendingTaskEvent(task, PendingTaskEvent.EVENT_VANISHED); + if (pendingVanished != null) { + // This task will vanish before this callback so just ignore. + return false; + } + + PendingTaskEvent pending = getPendingTaskEvent( + task, PendingTaskEvent.EVENT_ROOT_BACK_PRESSED); + if (pending == null) { + pending = new PendingTaskEvent(task, PendingTaskEvent.EVENT_ROOT_BACK_PRESSED); + } else { + // Pending already exist, remove and add for re-ordering. + mPendingTaskEvents.remove(pending); + } + mPendingTaskEvents.add(pending); return true; } + @Nullable + private PendingTaskEvent getPendingTaskEvent(Task task, int type) { + for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) { + PendingTaskEvent entry = mPendingTaskEvents.get(i); + if (task.mTaskId == entry.mTask.mTaskId && type == entry.mEventType) { + return entry; + } + } + return null; + } + + @VisibleForTesting + @Nullable + PendingTaskEvent getPendingLifecycleTaskEvent(Task task) { + for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) { + PendingTaskEvent entry = mPendingTaskEvents.get(i); + if (task.mTaskId == entry.mTask.mTaskId && entry.isLifecycleEvent()) { + return entry; + } + } + return null; + } + public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.print(prefix); pw.println("TaskOrganizerController:"); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index ed90cc759495..513fa70b1965 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -370,14 +370,26 @@ class TaskSnapshotController { SurfaceControl[] excludeLayers; final WindowState imeWindow = task.getDisplayContent().mInputMethodWindow; // Exclude IME window snapshot when IME isn't proper to attach to app. - if (imeWindow != null && imeWindow.getSurfaceControl() != null - && !task.getDisplayContent().isImeAttachedToApp()) { - excludeLayers = new SurfaceControl[1]; + final boolean excludeIme = imeWindow != null && imeWindow.getSurfaceControl() != null + && !task.getDisplayContent().isImeAttachedToApp(); + final WindowState navWindow = + task.getDisplayContent().getDisplayPolicy().getNavigationBar(); + // If config_attachNavBarToAppDuringTransition is true, the nav bar will be reparent to the + // the swiped app when entering recent app, therefore the task will contain the navigation + // bar and we should exclude it from snapshot. + final boolean excludeNavBar = navWindow != null; + if (excludeIme && excludeNavBar) { + excludeLayers = new SurfaceControl[2]; excludeLayers[0] = imeWindow.getSurfaceControl(); + excludeLayers[1] = navWindow.getSurfaceControl(); + } else if (excludeIme || excludeNavBar) { + excludeLayers = new SurfaceControl[1]; + excludeLayers[0] = + excludeIme ? imeWindow.getSurfaceControl() : navWindow.getSurfaceControl(); } else { excludeLayers = new SurfaceControl[0]; - builder.setHasImeSurface(imeWindow != null && imeWindow.isDrawn()); } + builder.setHasImeSurface(!excludeIme && imeWindow != null && imeWindow.isDrawn()); final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = SurfaceControl.captureLayersExcluding( task.getSurfaceControl(), mTmpRect, scaleFraction, diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index b37e3c42f568..46aea23beaf6 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -50,6 +50,7 @@ import android.util.Slog; import android.view.SurfaceControl; import android.view.WindowManager; import android.view.animation.Animation; +import android.window.IRemoteTransition; import android.window.TransitionInfo; import com.android.internal.annotations.VisibleForTesting; @@ -101,6 +102,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe private @WindowManager.TransitionFlags int mFlags; private final TransitionController mController; private final BLASTSyncEngine mSyncEngine; + private IRemoteTransition mRemoteTransition = null; /** * This is a leash to put animating surfaces into flatly without clipping/ordering issues. It @@ -235,8 +237,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe if (target.getParent() != null) { // Ensure surfaceControls are re-parented back into the hierarchy. t.reparent(target.getSurfaceControl(), target.getParent().getSurfaceControl()); + t.setLayer(target.getSurfaceControl(), target.getLastLayer()); + // TODO(shell-transitions): Once all remotables have been moved, see if there is + // a more appropriate place to do the following. This may + // involve passing an SF transaction from shell on finish. target.getRelativePosition(tmpPos); t.setPosition(target.getSurfaceControl(), tmpPos.x, tmpPos.y); + t.setCornerRadius(target.getSurfaceControl(), 0); + t.setShadowRadius(target.getSurfaceControl(), 0); displays.add(target.getDisplayContent()); } } @@ -283,6 +291,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mSyncEngine.abort(mSyncId); } + void setRemoteTransition(IRemoteTransition remoteTransition) { + mRemoteTransition = remoteTransition; + } + + IRemoteTransition getRemoteTransition() { + return mRemoteTransition; + } + @Override public void onTransactionReady(int syncId, SurfaceControl.Transaction transaction) { if (syncId != mSyncId) { diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 0fe0afa54dd3..5f46ffe604a6 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -26,7 +26,9 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; import android.view.WindowManager; +import android.window.IRemoteTransition; import android.window.ITransitionPlayer; +import android.window.TransitionRequestInfo; import com.android.internal.protolog.ProtoLogGroup; import com.android.internal.protolog.common.ProtoLog; @@ -140,7 +142,7 @@ class TransitionController { } /** - * @see #requestTransitionIfNeeded(int, int) + * @see #requestTransitionIfNeeded(int, int, WindowContainer, IRemoteTransition) */ @Nullable Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type, @@ -148,9 +150,19 @@ class TransitionController { return requestTransitionIfNeeded(type, 0 /* flags */, trigger); } + /** + * @see #requestTransitionIfNeeded(int, int, WindowContainer, IRemoteTransition) + */ + @Nullable + Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type, + @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger) { + return requestTransitionIfNeeded(type, flags, trigger, null /* remote */); + } + private static boolean isExistenceType(@WindowManager.TransitionType int type) { return type == TRANSIT_OPEN || type == TRANSIT_CLOSE; } + /** * If a transition isn't requested yet, creates one and asks the TransitionPlayer (Shell) to * start it. Collection can start immediately. @@ -159,7 +171,8 @@ class TransitionController { */ @Nullable Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type, - @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger) { + @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger, + @Nullable IRemoteTransition remoteTransition) { if (mTransitionPlayer == null) { return null; } @@ -169,7 +182,7 @@ class TransitionController { mCollectingTransition.setReady(false); } else { newTransition = requestStartTransition(createTransition(type, flags), - trigger != null ? trigger.asTask() : null); + trigger != null ? trigger.asTask() : null, remoteTransition); } if (trigger != null) { if (isExistenceType(type)) { @@ -183,7 +196,8 @@ class TransitionController { /** Asks the transition player (shell) to start a created but not yet started transition. */ @NonNull - Transition requestStartTransition(@NonNull Transition transition, @Nullable Task startTask) { + Transition requestStartTransition(@NonNull Transition transition, @Nullable Task startTask, + @Nullable IRemoteTransition remoteTransition) { try { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Requesting StartTransition: %s", transition); @@ -192,7 +206,8 @@ class TransitionController { info = new ActivityManager.RunningTaskInfo(); startTask.fillTaskInfo(info); } - mTransitionPlayer.requestStartTransition(transition.mType, transition, info); + mTransitionPlayer.requestStartTransition(transition, new TransitionRequestInfo( + transition.mType, info, remoteTransition)); } catch (RemoteException e) { Slog.e(TAG, "Error requesting transition", e); transition.start(); diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index c4eb63581153..52ed2788d795 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -222,6 +222,7 @@ public class WindowAnimator { mService.destroyPreservedSurfaceLocked(); + mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); executeAfterPrepareSurfacesRunnables(); if (DEBUG_WINDOW_TRACE) { diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index be1f7e16cd3c..1509ff67a529 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -264,11 +264,18 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub for (int i = 0, n = hops.size(); i < n; ++i) { final WindowContainerTransaction.HierarchyOp hop = hops.get(i); switch (hop.getType()) { - case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: - final Task task = WindowContainer.fromBinder(hop.getContainer()).asTask(); + case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: { + final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); + final Task task = wc != null ? wc.asTask() : null; + if (task != null) { task.getDisplayArea().setLaunchRootTask(task, hop.getWindowingModes(), hop.getActivityTypes()); + } else { + throw new IllegalArgumentException( + "Cannot set non-task as launch root: " + wc); + } break; + } case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId); break; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 124e120e310e..13b976524bb9 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -213,6 +213,7 @@ import android.os.Trace; import android.os.WorkSource; import android.provider.Settings; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.MergedConfiguration; @@ -645,9 +646,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP boolean mSeamlesslyRotated = false; /** - * Indicates if this window is behind IME. Only windows behind IME can get insets from IME. + * The insets state of sources provided by windows above the current window. */ - boolean mBehindIme = false; + InsetsState mAboveInsetsState = new InsetsState(); + + /** + * The insets sources provided by this window. + */ + ArrayMap<Integer, InsetsSource> mProvidedInsetsSources = new ArrayMap<>(); /** * Surface insets from the previous call to relayout(), used to track @@ -2097,7 +2103,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return getDisplayContent().getBounds().equals(getBounds()); } - private boolean matchesRootDisplayAreaBounds() { + boolean matchesRootDisplayAreaBounds() { RootDisplayArea root = getRootDisplayArea(); if (root == null || root == getDisplayContent()) { return matchesDisplayBounds(); diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 13c67523a7d6..dc15b0749bb1 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -161,7 +161,7 @@ static struct { jmethodID size; } gSparseArrayClassInfo; -struct InputSensorInfoOffsets { +static struct InputSensorInfoOffsets { jclass clazz; // fields jfieldID name; diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp index 4e1a23416330..a5311f33eb36 100644 --- a/services/core/jni/com_android_server_tv_TvInputHal.cpp +++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp @@ -261,7 +261,7 @@ public: void onDeviceAvailable(const TvInputDeviceInfo& info); void onDeviceUnavailable(int deviceId); - void onStreamConfigurationsChanged(int deviceId); + void onStreamConfigurationsChanged(int deviceId, int cableConnectionStatus); void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded); private: @@ -519,7 +519,7 @@ void JTvInputHal::onDeviceUnavailable(int deviceId) { deviceId); } -void JTvInputHal::onStreamConfigurationsChanged(int deviceId) { +void JTvInputHal::onStreamConfigurationsChanged(int deviceId, int cableConnectionStatus) { { Mutex::Autolock autoLock(&mLock); KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); @@ -529,10 +529,8 @@ void JTvInputHal::onStreamConfigurationsChanged(int deviceId) { connections.clear(); } JNIEnv* env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod( - mThiz, - gTvInputHalClassInfo.streamConfigsChanged, - deviceId); + env->CallVoidMethod(mThiz, gTvInputHalClassInfo.streamConfigsChanged, deviceId, + cableConnectionStatus); } void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded) { @@ -572,7 +570,8 @@ void JTvInputHal::NotifyHandler::handleMessage(const Message& message) { mHal->onDeviceUnavailable(mEvent.deviceInfo.deviceId); } break; case TvInputEventType::STREAM_CONFIGURATIONS_CHANGED: { - mHal->onStreamConfigurationsChanged(mEvent.deviceInfo.deviceId); + int cableConnectionStatus = static_cast<int>(mEvent.deviceInfo.cableConnectionStatus); + mHal->onStreamConfigurationsChanged(mEvent.deviceInfo.deviceId, cableConnectionStatus); } break; default: ALOGE("Unrecognizable event"); @@ -688,9 +687,8 @@ int register_android_server_tv_TvInputHal(JNIEnv* env) { "deviceAvailableFromNative", "(Landroid/media/tv/TvInputHardwareInfo;)V"); GET_METHOD_ID( gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V"); - GET_METHOD_ID( - gTvInputHalClassInfo.streamConfigsChanged, clazz, - "streamConfigsChangedFromNative", "(I)V"); + GET_METHOD_ID(gTvInputHalClassInfo.streamConfigsChanged, clazz, + "streamConfigsChangedFromNative", "(II)V"); GET_METHOD_ID( gTvInputHalClassInfo.firstFrameCaptured, clazz, "firstFrameCapturedFromNative", "(II)V"); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 737bf9523fe6..7df90166420c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3383,6 +3383,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REMOVE_ACTIVE_ADMIN); enforceUserUnlocked(userHandle); synchronized (getLockObject()) { @@ -5143,6 +5144,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } final CallerIdentity caller = getCallerIdentity(admin, callerPackage); Preconditions.checkCallAuthorization(canManageCaCerts(caller)); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_INSTALL_CA_CERT); final String alias = mInjector.binderWithCleanCallingIdentity(() -> { String installedAlias = mCertificateMonitor.installCaCert( @@ -5174,6 +5176,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } final CallerIdentity caller = getCallerIdentity(admin, callerPackage); Preconditions.checkCallAuthorization(canManageCaCerts(caller)); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_UNINSTALL_CA_CERT); mInjector.binderWithCleanCallingIdentity(() -> { mCertificateMonitor.uninstallCaCerts(caller.getUserHandle(), aliases); @@ -5203,6 +5206,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization((caller.hasAdminComponent() && (isProfileOwner(caller) || isDeviceOwner(caller))) || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp))); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_INSTALL_KEY_PAIR); final long id = mInjector.binderClearCallingIdentity(); try { @@ -5260,6 +5264,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization((caller.hasAdminComponent() && (isProfileOwner(caller) || isDeviceOwner(caller))) || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp))); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REMOVE_KEY_PAIR); final long id = Binder.clearCallingIdentity(); try { @@ -6157,6 +6162,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_ALWAYS_ON_VPN_PACKAGE); final int userId = caller.getUserId(); mInjector.binderWithCleanCallingIdentity(() -> { @@ -6500,6 +6506,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager + .OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY); final int frpManagementAgentUid = getFrpManagementAgentUidOrThrow(); synchronized (getLockObject()) { @@ -7397,6 +7405,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isDeviceOwner(caller)); checkAllUsersAreAffiliatedWithDevice(); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REQUEST_BUGREPORT); if (mBugreportCollectionManager.requestBugreport()) { DevicePolicyEventLogger @@ -7506,6 +7515,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (parent) { Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller)); } + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_CAMERA_DISABLED); final int userHandle = caller.getUserId(); synchronized (getLockObject()) { @@ -8640,11 +8650,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (isCallerProfileOwnerOrDelegate && isProfileOwnerOfOrganizationOwnedDevice(userId)) { return true; } - //TODO(b/130844684): Temporarily allow profile owner on non-organization-owned devices - //to read device identifiers. - if (isCallerProfileOwnerOrDelegate) { - return true; - } return false; } @@ -9256,6 +9261,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_RESTRICTIONS_PROVIDER); synchronized (getLockObject()) { int userHandle = caller.getUserId(); @@ -11431,6 +11437,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_MASTER_VOLUME_MUTED); synchronized (getLockObject()) { setUserRestriction(who, UserManager.DISALLOW_UNMUTE_DEVICE, on, /* parent */ false); @@ -12407,6 +12414,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization((caller.hasAdminComponent() && (isProfileOwner(caller) || isDeviceOwner(caller))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT))); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_PERMISSION_POLICY); synchronized (getLockObject()) { DevicePolicyData userPolicy = getUserData(caller.getUserId()); @@ -12442,6 +12450,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization((caller.hasAdminComponent() && (isProfileOwner(caller) || isDeviceOwner(caller))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT))); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_PERMISSION_GRANT_STATE); synchronized (getLockObject()) { long ident = mInjector.binderClearCallingIdentity(); @@ -14418,6 +14427,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(admin); Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_CLEAR_APPLICATION_USER_DATA); long ident = mInjector.binderClearCallingIdentity(); try { @@ -14449,6 +14459,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(admin); Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOGOUT_ENABLED); synchronized (getLockObject()) { ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); @@ -14816,6 +14827,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_OVERRIDE_APNS_ENABLED); + setOverrideApnsEnabledUnchecked(enabled); } @@ -14917,6 +14930,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_GLOBAL_PRIVATE_DNS); switch (mode) { case PRIVATE_DNS_MODE_OPPORTUNISTIC: @@ -14989,6 +15003,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(admin); Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_INSTALL_SYSTEM_UPDATE); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.INSTALL_SYSTEM_UPDATE) diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 94df1851aa87..57b3e8da8885 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -160,7 +160,7 @@ import com.android.server.pm.UserManagerService; import com.android.server.pm.dex.SystemServerDexLoadReporter; import com.android.server.policy.PermissionPolicyService; import com.android.server.policy.PhoneWindowManager; -import com.android.server.policy.role.LegacyRoleResolutionPolicy; +import com.android.server.policy.role.LegacyRoleStateProviderImpl; import com.android.server.power.PowerManagerService; import com.android.server.power.ShutdownThread; import com.android.server.power.ThermalManagerService; @@ -2021,7 +2021,7 @@ public final class SystemServer implements Dumpable { // Grants default permissions and defines roles t.traceBegin("StartRoleManagerService"); mSystemServiceManager.startService(new RoleManagerService( - mSystemContext, new LegacyRoleResolutionPolicy(mSystemContext))); + mSystemContext, new LegacyRoleStateProviderImpl(mSystemContext))); t.traceEnd(); // We need to always start this service, regardless of whether the diff --git a/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java b/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java index e4daddca4564..0a85f7fc3662 100644 --- a/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java +++ b/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java @@ -85,8 +85,10 @@ abstract class AbstractProtoDiskReadWriter<T> { } @WorkerThread - synchronized void delete(@NonNull String fileName) { - mScheduledFileDataMap.remove(fileName); + void delete(@NonNull String fileName) { + synchronized (this) { + mScheduledFileDataMap.remove(fileName); + } final File file = getFile(fileName); if (!file.exists()) { return; @@ -136,28 +138,6 @@ abstract class AbstractProtoDiskReadWriter<T> { } /** - * Reads all files in directory and returns a map with file names as keys and parsed file - * contents as values. - */ - @WorkerThread - @Nullable - Map<String, T> readAll() { - File[] files = mRootDir.listFiles(File::isFile); - if (files == null) { - return null; - } - - Map<String, T> results = new ArrayMap<>(); - for (File file : files) { - T result = parseFile(file); - if (result != null) { - results.put(file.getName(), result); - } - } - return results; - } - - /** * Schedules the specified data to be flushed to a file in the future. Subsequent * calls for the same file before the flush occurs will replace the previous data but will not * reset when the flush will occur. All unique files will be flushed at the same time. diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java index 28e3d4b5b744..6faeb8053621 100644 --- a/services/people/java/com/android/server/people/data/ConversationStore.java +++ b/services/people/java/com/android/server/people/data/ConversationStore.java @@ -90,7 +90,7 @@ class ConversationStore { * after the device powers on and the user has been unlocked. */ @WorkerThread - synchronized void loadConversationsFromDisk() { + void loadConversationsFromDisk() { ConversationInfosProtoDiskReadWriter conversationInfosProtoDiskReadWriter = getConversationInfosProtoDiskReadWriter(); if (conversationInfosProtoDiskReadWriter == null) { @@ -111,54 +111,64 @@ class ConversationStore { * powering off. */ @MainThread - synchronized void saveConversationsToDisk() { + void saveConversationsToDisk() { ConversationInfosProtoDiskReadWriter conversationInfosProtoDiskReadWriter = getConversationInfosProtoDiskReadWriter(); if (conversationInfosProtoDiskReadWriter != null) { - conversationInfosProtoDiskReadWriter.saveConversationsImmediately( - new ArrayList<>(mConversationInfoMap.values())); + List<ConversationInfo> conversations; + synchronized (this) { + conversations = new ArrayList<>(mConversationInfoMap.values()); + } + conversationInfosProtoDiskReadWriter.saveConversationsImmediately(conversations); } } @MainThread - synchronized void addOrUpdate(@NonNull ConversationInfo conversationInfo) { + void addOrUpdate(@NonNull ConversationInfo conversationInfo) { updateConversationsInMemory(conversationInfo); scheduleUpdateConversationsOnDisk(); } @MainThread @Nullable - synchronized ConversationInfo deleteConversation(@NonNull String shortcutId) { - ConversationInfo conversationInfo = mConversationInfoMap.remove(shortcutId); - if (conversationInfo == null) { - return null; - } + ConversationInfo deleteConversation(@NonNull String shortcutId) { + ConversationInfo conversationInfo; + synchronized (this) { + conversationInfo = mConversationInfoMap.remove(shortcutId); + if (conversationInfo == null) { + return null; + } - LocusId locusId = conversationInfo.getLocusId(); - if (locusId != null) { - mLocusIdToShortcutIdMap.remove(locusId); - } + LocusId locusId = conversationInfo.getLocusId(); + if (locusId != null) { + mLocusIdToShortcutIdMap.remove(locusId); + } - Uri contactUri = conversationInfo.getContactUri(); - if (contactUri != null) { - mContactUriToShortcutIdMap.remove(contactUri); - } + Uri contactUri = conversationInfo.getContactUri(); + if (contactUri != null) { + mContactUriToShortcutIdMap.remove(contactUri); + } - String phoneNumber = conversationInfo.getContactPhoneNumber(); - if (phoneNumber != null) { - mPhoneNumberToShortcutIdMap.remove(phoneNumber); - } + String phoneNumber = conversationInfo.getContactPhoneNumber(); + if (phoneNumber != null) { + mPhoneNumberToShortcutIdMap.remove(phoneNumber); + } - String notifChannelId = conversationInfo.getNotificationChannelId(); - if (notifChannelId != null) { - mNotifChannelIdToShortcutIdMap.remove(notifChannelId); + String notifChannelId = conversationInfo.getNotificationChannelId(); + if (notifChannelId != null) { + mNotifChannelIdToShortcutIdMap.remove(notifChannelId); + } } scheduleUpdateConversationsOnDisk(); return conversationInfo; } - synchronized void forAllConversations(@NonNull Consumer<ConversationInfo> consumer) { - for (ConversationInfo ci : mConversationInfoMap.values()) { + void forAllConversations(@NonNull Consumer<ConversationInfo> consumer) { + List<ConversationInfo> conversations; + synchronized (this) { + conversations = new ArrayList<>(mConversationInfoMap.values()); + } + for (ConversationInfo ci : conversations) { consumer.accept(ci); } } @@ -184,16 +194,19 @@ class ConversationStore { } @Nullable - ConversationInfo getConversationByNotificationChannelId(@NonNull String notifChannelId) { + synchronized ConversationInfo getConversationByNotificationChannelId( + @NonNull String notifChannelId) { return getConversation(mNotifChannelIdToShortcutIdMap.get(notifChannelId)); } - synchronized void onDestroy() { - mConversationInfoMap.clear(); - mContactUriToShortcutIdMap.clear(); - mLocusIdToShortcutIdMap.clear(); - mNotifChannelIdToShortcutIdMap.clear(); - mPhoneNumberToShortcutIdMap.clear(); + void onDestroy() { + synchronized (this) { + mConversationInfoMap.clear(); + mContactUriToShortcutIdMap.clear(); + mLocusIdToShortcutIdMap.clear(); + mNotifChannelIdToShortcutIdMap.clear(); + mPhoneNumberToShortcutIdMap.clear(); + } ConversationInfosProtoDiskReadWriter writer = getConversationInfosProtoDiskReadWriter(); if (writer != null) { writer.deleteConversationsFile(); @@ -201,22 +214,21 @@ class ConversationStore { } @Nullable - synchronized byte[] getBackupPayload() { + byte[] getBackupPayload() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream conversationInfosOut = new DataOutputStream(baos); - for (ConversationInfo conversationInfo : mConversationInfoMap.values()) { + forAllConversations(conversationInfo -> { byte[] backupPayload = conversationInfo.getBackupPayload(); if (backupPayload == null) { - continue; + return; } try { conversationInfosOut.writeInt(backupPayload.length); conversationInfosOut.write(backupPayload); } catch (IOException e) { Slog.e(TAG, "Failed to write conversation info to backup payload.", e); - return null; } - } + }); try { conversationInfosOut.writeInt(CONVERSATION_INFOS_END_TOKEN); } catch (IOException e) { @@ -226,7 +238,7 @@ class ConversationStore { return baos.toByteArray(); } - synchronized void restore(@NonNull byte[] payload) { + void restore(@NonNull byte[] payload) { DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload)); try { for (int conversationInfoSize = in.readInt(); @@ -245,7 +257,6 @@ class ConversationStore { } } - @MainThread private synchronized void updateConversationsInMemory( @NonNull ConversationInfo conversationInfo) { mConversationInfoMap.put(conversationInfo.getShortcutId(), conversationInfo); @@ -273,12 +284,15 @@ class ConversationStore { /** Schedules a dump of all conversations onto disk, overwriting existing values. */ @MainThread - private synchronized void scheduleUpdateConversationsOnDisk() { + private void scheduleUpdateConversationsOnDisk() { ConversationInfosProtoDiskReadWriter conversationInfosProtoDiskReadWriter = getConversationInfosProtoDiskReadWriter(); if (conversationInfosProtoDiskReadWriter != null) { - conversationInfosProtoDiskReadWriter.scheduleConversationsSave( - new ArrayList<>(mConversationInfoMap.values())); + List<ConversationInfo> conversations; + synchronized (this) { + conversations = new ArrayList<>(mConversationInfoMap.values()); + } + conversationInfosProtoDiskReadWriter.scheduleConversationsSave(conversations); } } diff --git a/services/searchui/OWNERS b/services/searchui/OWNERS new file mode 100644 index 000000000000..92835c2b0626 --- /dev/null +++ b/services/searchui/OWNERS @@ -0,0 +1,2 @@ +hyunyoungs@google.com +sfufa@google.com diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml index fbde1d2cf1cf..e4b650cad055 100644 --- a/services/tests/mockingservicestests/AndroidManifest.xml +++ b/services/tests/mockingservicestests/AndroidManifest.xml @@ -21,11 +21,13 @@ <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/> <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/> + <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/> <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> <uses-permission android:name="android.permission.HARDWARE_TEST"/> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> <uses-permission android:name="android.permission.MANAGE_APPOPS"/> <uses-permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS"/> + <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/> <!-- needed by MasterClearReceiverTest to display a system dialog --> <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/> diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java index f4c6918c0e96..e99113d1296f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java @@ -351,6 +351,7 @@ public class DeviceIdleControllerTest { // them after each test, otherwise, subsequent tests will fail. LocalServices.removeServiceForTest(AppStateTracker.class); LocalServices.removeServiceForTest(DeviceIdleInternal.class); + LocalServices.removeServiceForTest(PowerAllowlistInternal.class); } @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java new file mode 100644 index 000000000000..37103965de3b --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManagerInternal; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Process; +import android.provider.DeviceConfig; + +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.server.LocalServices; +import com.android.server.ServiceThread; +import com.android.server.appop.AppOpsService; +import com.android.server.testables.TestableDeviceConfig; +import com.android.server.wm.ActivityTaskManagerService; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.File; +import java.time.Duration; +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; + +/** + * Tests for {@link CachedAppOptimizer}. + * + * Build/Install/Run: + * atest FrameworksMockingServicesTests:CacheOomRankerTest + */ +@RunWith(MockitoJUnitRunner.class) +public class CacheOomRankerTest { + + @Mock + private AppOpsService mAppOpsService; + private Handler mHandler; + private ActivityManagerService mAms; + + @Mock + private PackageManagerInternal mPackageManagerInt; + + @Rule + public final TestableDeviceConfig.TestableDeviceConfigRule + mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule(); + @Rule + public final ApplicationExitInfoTest.ServiceThreadRule + mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule(); + + private int mNextPid = 10000; + private int mNextUid = 30000; + private int mNextPackageUid = 40000; + private int mNextPackageName = 1; + + private TestExecutor mExecutor = new TestExecutor(); + private CacheOomRanker mCacheOomRanker = new CacheOomRanker(); + + @Before + public void setUp() { + HandlerThread handlerThread = new HandlerThread(""); + handlerThread.start(); + mHandler = new Handler(handlerThread.getLooper()); + /* allowIo */ + ServiceThread thread = new ServiceThread("TestServiceThread", + Process.THREAD_PRIORITY_DEFAULT, + true /* allowIo */); + thread.start(); + Context context = InstrumentationRegistry.getInstrumentation().getContext(); + mAms = new ActivityManagerService( + new TestInjector(context), mServiceThreadRule.getThread()); + mAms.mActivityTaskManager = new ActivityTaskManagerService(context); + mAms.mActivityTaskManager.initialize(null, null, context.getMainLooper()); + mAms.mAtmInternal = spy(mAms.mActivityTaskManager.getAtmInternal()); + mAms.mPackageManagerInt = mPackageManagerInt; + doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent(); + LocalServices.removeServiceForTest(PackageManagerInternal.class); + LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt); + + mCacheOomRanker.init(mExecutor); + } + + @Test + public void init_listensForConfigChanges() throws InterruptedException { + mExecutor.init(); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CacheOomRanker.KEY_USE_OOM_RE_RANKING, + Boolean.TRUE.toString(), true); + mExecutor.waitForLatch(); + assertThat(mCacheOomRanker.useOomReranking()).isTrue(); + mExecutor.init(); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CacheOomRanker.KEY_USE_OOM_RE_RANKING, Boolean.FALSE.toString(), false); + mExecutor.waitForLatch(); + assertThat(mCacheOomRanker.useOomReranking()).isFalse(); + + mExecutor.init(); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CacheOomRanker.KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK, + Integer.toString(CacheOomRanker.DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK + 2), + false); + mExecutor.waitForLatch(); + assertThat(mCacheOomRanker.getNumberToReRank()) + .isEqualTo(CacheOomRanker.DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK + 2); + + mExecutor.init(); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT, + Float.toString(CacheOomRanker.DEFAULT_OOM_RE_RANKING_LRU_WEIGHT + 0.1f), + false); + mExecutor.waitForLatch(); + assertThat(mCacheOomRanker.mLruWeight) + .isEqualTo(CacheOomRanker.DEFAULT_OOM_RE_RANKING_LRU_WEIGHT + 0.1f); + + mExecutor.init(); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CacheOomRanker.KEY_OOM_RE_RANKING_RSS_WEIGHT, + Float.toString(CacheOomRanker.DEFAULT_OOM_RE_RANKING_RSS_WEIGHT - 0.1f), + false); + mExecutor.waitForLatch(); + assertThat(mCacheOomRanker.mRssWeight) + .isEqualTo(CacheOomRanker.DEFAULT_OOM_RE_RANKING_RSS_WEIGHT - 0.1f); + + mExecutor.init(); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CacheOomRanker.KEY_OOM_RE_RANKING_USES_WEIGHT, + Float.toString(CacheOomRanker.DEFAULT_OOM_RE_RANKING_USES_WEIGHT + 0.2f), + false); + mExecutor.waitForLatch(); + assertThat(mCacheOomRanker.mUsesWeight) + .isEqualTo(CacheOomRanker.DEFAULT_OOM_RE_RANKING_USES_WEIGHT + 0.2f); + } + + @Test + public void reRankLruCachedApps_lruImpactsOrdering() throws InterruptedException { + setConfig(/* numberToReRank= */ 5, + /* usesWeight= */ 0.0f, + /* pssWeight= */ 0.0f, + /* lruWeight= */1.0f); + + ProcessList list = new ProcessList(); + ArrayList<ProcessRecord> processList = list.mLruProcesses; + ProcessRecord lastUsed40MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000); + processList.add(lastUsed40MinutesAgo); + ProcessRecord lastUsed42MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000); + processList.add(lastUsed42MinutesAgo); + ProcessRecord lastUsed60MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(60).toMillis(), 1024L, 10000); + processList.add(lastUsed60MinutesAgo); + ProcessRecord lastUsed15MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10); + processList.add(lastUsed15MinutesAgo); + ProcessRecord lastUsed17MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(17).toMillis(), 1024L, 20); + processList.add(lastUsed17MinutesAgo); + // Only re-ranking 5 entries so this should stay in most recent position. + ProcessRecord lastUsed30MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(30).toMillis(), 1024L, 20); + processList.add(lastUsed30MinutesAgo); + + mCacheOomRanker.reRankLruCachedApps(list); + + // First 5 ordered by least recently used first, then last processes position unchanged. + assertThat(processList).containsExactly(lastUsed60MinutesAgo, lastUsed42MinutesAgo, + lastUsed40MinutesAgo, lastUsed17MinutesAgo, lastUsed15MinutesAgo, + lastUsed30MinutesAgo); + } + + @Test + public void reRankLruCachedApps_rssImpactsOrdering() throws InterruptedException { + setConfig(/* numberToReRank= */ 6, + /* usesWeight= */ 0.0f, + /* pssWeight= */ 1.0f, + /* lruWeight= */ 0.0f); + + ProcessList list = new ProcessList(); + ArrayList<ProcessRecord> processList = list.mLruProcesses; + ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000); + processList.add(rss10k); + ProcessRecord rss20k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000); + processList.add(rss20k); + ProcessRecord rss1k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(60).toMillis(), 1024L, 10000); + processList.add(rss1k); + ProcessRecord rss100k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10); + processList.add(rss100k); + ProcessRecord rss2k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20); + processList.add(rss2k); + ProcessRecord rss15k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(30).toMillis(), 15 * 1024L, 20); + processList.add(rss15k); + // Only re-ranking 6 entries so this should stay in most recent position. + ProcessRecord rss16k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(30).toMillis(), 16 * 1024L, 20); + processList.add(rss16k); + + mCacheOomRanker.reRankLruCachedApps(list); + + // First 6 ordered by largest pss, then last processes position unchanged. + assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k, + rss16k); + } + + @Test + public void reRankLruCachedApps_usesImpactsOrdering() throws InterruptedException { + setConfig(/* numberToReRank= */ 4, + /* usesWeight= */ 1.0f, + /* pssWeight= */ 0.0f, + /* lruWeight= */ 0.0f); + + ProcessList list = new ProcessList(); + list.mLruProcessServiceStart = 1; + ArrayList<ProcessRecord> processList = list.mLruProcesses; + ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000); + processList.add(used1000); + ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000); + processList.add(used2000); + ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10); + processList.add(used10); + ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20); + processList.add(used20); + // Only re-ranking 6 entries so last two should stay in most recent position. + ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500); + processList.add(used500); + ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200); + processList.add(used200); + + mCacheOomRanker.reRankLruCachedApps(list); + + // First 4 ordered by uses, then last processes position unchanged. + assertThat(processList).containsExactly(used10, used20, used1000, used2000, used500, + used200); + } + + @Test + public void reRankLruCachedApps_notEnoughProcesses() throws InterruptedException { + setConfig(/* numberToReRank= */ 4, + /* usesWeight= */ 0.5f, + /* pssWeight= */ 0.2f, + /* lruWeight= */ 0.3f); + + ProcessList list = new ProcessList(); + ArrayList<ProcessRecord> processList = list.mLruProcesses; + ProcessRecord unknownAdj1 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000); + processList.add(unknownAdj1); + ProcessRecord unknownAdj2 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000); + processList.add(unknownAdj2); + ProcessRecord unknownAdj3 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10); + processList.add(unknownAdj3); + ProcessRecord foregroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ, + Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20); + processList.add(foregroundAdj); + ProcessRecord serviceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ, + Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500); + processList.add(serviceAdj); + ProcessRecord systemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ, + Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200); + processList.add(systemAdj); + + // 6 Processes but only 3 in eligible for cache so no re-ranking. + mCacheOomRanker.reRankLruCachedApps(list); + + // All positions unchanged. + assertThat(processList).containsExactly(unknownAdj1, unknownAdj2, unknownAdj3, + foregroundAdj, serviceAdj, systemAdj); + } + + @Test + public void reRankLruCachedApps_notEnoughNonServiceProcesses() throws InterruptedException { + setConfig(/* numberToReRank= */ 4, + /* usesWeight= */ 1.0f, + /* pssWeight= */ 0.0f, + /* lruWeight= */ 0.0f); + + ProcessList list = new ProcessList(); + list.mLruProcessServiceStart = 4; + ArrayList<ProcessRecord> processList = list.mLruProcesses; + ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000); + processList.add(used1000); + ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000); + processList.add(used2000); + ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10); + processList.add(used10); + ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20); + processList.add(used20); + ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500); + processList.add(used500); + ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, + Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200); + processList.add(used200); + + mCacheOomRanker.reRankLruCachedApps(list); + + // All positions unchanged. + assertThat(processList).containsExactly(used1000, used2000, used10, used20, used500, + used200); + } + + private void setConfig(int numberToReRank, float useWeight, float pssWeight, float lruWeight) + throws InterruptedException { + mExecutor.init(4); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CacheOomRanker.KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK, + Integer.toString(numberToReRank), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT, + Float.toString(lruWeight), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CacheOomRanker.KEY_OOM_RE_RANKING_RSS_WEIGHT, + Float.toString(pssWeight), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CacheOomRanker.KEY_OOM_RE_RANKING_USES_WEIGHT, + Float.toString(useWeight), + false); + mExecutor.waitForLatch(); + assertThat(mCacheOomRanker.getNumberToReRank()).isEqualTo(numberToReRank); + assertThat(mCacheOomRanker.mRssWeight).isEqualTo(pssWeight); + assertThat(mCacheOomRanker.mUsesWeight).isEqualTo(useWeight); + assertThat(mCacheOomRanker.mLruWeight).isEqualTo(lruWeight); + } + + private ProcessRecord nextProcessRecord(int setAdj, long lastActivityTime, long lastRss, + int returnedToCacheCount) { + ApplicationInfo ai = new ApplicationInfo(); + ai.packageName = "a.package.name" + mNextPackageName++; + ProcessRecord app = new ProcessRecord(mAms, ai, ai.packageName + ":process", mNextUid++); + app.pid = mNextPid++; + app.info.uid = mNextPackageUid++; + // Exact value does not mater, it can be any state for which compaction is allowed. + app.setProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE; + app.setAdj = setAdj; + app.lastActivityTime = lastActivityTime; + app.mLastRss = lastRss; + app.setCached(false); + for (int i = 0; i < returnedToCacheCount; ++i) { + app.setCached(false); + app.setCached(true); + } + return app; + } + + private class TestExecutor implements Executor { + private CountDownLatch mLatch; + + private void init(int count) { + mLatch = new CountDownLatch(count); + } + + private void init() { + init(1); + } + + private void waitForLatch() throws InterruptedException { + mLatch.await(5, TimeUnit.SECONDS); + } + + @Override + public void execute(Runnable command) { + command.run(); + mLatch.countDown(); + } + } + + private class TestInjector extends ActivityManagerService.Injector { + private TestInjector(Context context) { + super(context); + } + + @Override + public AppOpsService getAppOpsService(File file, Handler handler) { + return mAppOpsService; + } + + @Override + public Handler getUiHandler(ActivityManagerService service) { + return mHandler; + } + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java index dcbf8c02edf9..4effa4d445bb 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java @@ -59,6 +59,7 @@ import com.android.server.AppStateTracker; import com.android.server.AppStateTrackerImpl; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; +import com.android.server.PowerAllowlistInternal; import com.android.server.SystemServiceManager; import com.android.server.job.controllers.JobStatus; import com.android.server.usage.AppStandbyInternal; @@ -134,6 +135,8 @@ public class JobSchedulerServiceTest { when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class)); when(mContext.getResources()).thenReturn(mock(Resources.class)); // Called in QuotaController constructor. + doReturn(mock(PowerAllowlistInternal.class)) + .when(() -> LocalServices.getService(PowerAllowlistInternal.class)); IActivityManager activityManager = ActivityManager.getService(); spyOn(activityManager); try { diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index 1a65894f85b1..c4c9173536ad 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -80,6 +80,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; +import com.android.server.PowerAllowlistInternal; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobSchedulerService.Constants; import com.android.server.job.JobServiceContext; @@ -123,6 +124,7 @@ public class QuotaControllerTest { private QuotaController mQuotaController; private QuotaController.QcConstants mQcConstants; private int mSourceUid; + private PowerAllowlistInternal.TempAllowlistChangeListener mTempAllowlistListener; private IUidObserver mUidObserver; DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder; @@ -138,6 +140,8 @@ public class QuotaControllerTest { @Mock private PackageManagerInternal mPackageManagerInternal; @Mock + private PowerAllowlistInternal mPowerAllowlistInternal; + @Mock private UsageStatsManagerInternal mUsageStatsManager; private JobStore mJobStore; @@ -173,6 +177,8 @@ public class QuotaControllerTest { .when(() -> LocalServices.getService(BatteryManagerInternal.class)); doReturn(mUsageStatsManager) .when(() -> LocalServices.getService(UsageStatsManagerInternal.class)); + doReturn(mPowerAllowlistInternal) + .when(() -> LocalServices.getService(PowerAllowlistInternal.class)); // Used in JobStatus. doReturn(mPackageManagerInternal) .when(() -> LocalServices.getService(PackageManagerInternal.class)); @@ -211,11 +217,19 @@ public class QuotaControllerTest { ArgumentCaptor.forClass(BroadcastReceiver.class); ArgumentCaptor<IUidObserver> uidObserverCaptor = ArgumentCaptor.forClass(IUidObserver.class); + ArgumentCaptor<PowerAllowlistInternal.TempAllowlistChangeListener> taChangeCaptor = + ArgumentCaptor.forClass(PowerAllowlistInternal.TempAllowlistChangeListener.class); mQuotaController = new QuotaController(mJobSchedulerService, mock(BackgroundJobsController.class), mock(ConnectivityController.class)); - verify(mContext).registerReceiver(receiverCaptor.capture(), any()); + verify(mContext).registerReceiver(receiverCaptor.capture(), + ArgumentMatchers.argThat(filter -> + filter.hasAction(BatteryManager.ACTION_CHARGING) + && filter.hasAction(BatteryManager.ACTION_DISCHARGING))); mChargingReceiver = receiverCaptor.getValue(); + verify(mPowerAllowlistInternal) + .registerTempAllowlistChangeListener(taChangeCaptor.capture()); + mTempAllowlistListener = taChangeCaptor.getValue(); try { verify(activityManager).registerUidObserver( uidObserverCaptor.capture(), @@ -2385,6 +2399,8 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 87 * SECOND_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, 86 * SECOND_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, 85 * SECOND_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, + 84 * SECOND_IN_MILLIS); assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()); assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs()); @@ -2423,6 +2439,7 @@ public class QuotaControllerTest { assertEquals(87 * SECOND_IN_MILLIS, mQuotaController.getEJRewardTopAppMs()); assertEquals(86 * SECOND_IN_MILLIS, mQuotaController.getEJRewardInteractionMs()); assertEquals(85 * SECOND_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs()); + assertEquals(84 * SECOND_IN_MILLIS, mQuotaController.getEJTempAllowlistGracePeriodMs()); } @Test @@ -2462,6 +2479,7 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, -1); + setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, -1); assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()); assertEquals(0, mQuotaController.getInQuotaBufferMs()); @@ -2497,6 +2515,7 @@ public class QuotaControllerTest { assertEquals(10 * SECOND_IN_MILLIS, mQuotaController.getEJRewardTopAppMs()); assertEquals(5 * SECOND_IN_MILLIS, mQuotaController.getEJRewardInteractionMs()); assertEquals(0, mQuotaController.getEJRewardNotificationSeenMs()); + assertEquals(0, mQuotaController.getEJTempAllowlistGracePeriodMs()); // Invalid configurations. // In_QUOTA_BUFFER should never be greater than ALLOWED_TIME_PER_PERIOD @@ -2530,6 +2549,7 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, 25 * HOUR_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, 25 * HOUR_IN_MILLIS); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()); assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs()); @@ -2555,6 +2575,7 @@ public class QuotaControllerTest { assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardTopAppMs()); assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardInteractionMs()); assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs()); + assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJTempAllowlistGracePeriodMs()); } /** Tests that TimingSessions aren't saved when the device is charging. */ @@ -3145,6 +3166,119 @@ public class QuotaControllerTest { } /** + * Tests that Timers properly track regular sessions when an app is added and removed from the + * temp allowlist. + */ + @Test + public void testTimerTracking_TempAllowlisting() { + // None of these should be affected purely by the temp allowlist changing. + setDischarging(); + setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); + final long gracePeriodMs = 15 * SECOND_IN_MILLIS; + setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, gracePeriodMs); + Handler handler = mQuotaController.getHandler(); + spyOn(handler); + + JobStatus job1 = createJobStatus("testTimerTracking_TempAllowlisting", 1); + JobStatus job2 = createJobStatus("testTimerTracking_TempAllowlisting", 2); + JobStatus job3 = createJobStatus("testTimerTracking_TempAllowlisting", 3); + JobStatus job4 = createJobStatus("testTimerTracking_TempAllowlisting", 4); + JobStatus job5 = createJobStatus("testTimerTracking_TempAllowlisting", 5); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job1, null); + } + assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + List<TimingSession> expected = new ArrayList<>(); + + long start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(job1); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job1, job1, true); + } + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, + mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(SECOND_IN_MILLIS); + + // Job starts after app is added to temp allowlist and stops before removal. + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + mTempAllowlistListener.onAppAdded(mSourceUid); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job2, null); + mQuotaController.prepareForExecutionLocked(job2); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job2, null, false); + } + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, + mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Job starts after app is added to temp allowlist and stops after removal, + // before grace period ends. + mTempAllowlistListener.onAppAdded(mSourceUid); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job3, null); + mQuotaController.prepareForExecutionLocked(job3); + } + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mTempAllowlistListener.onAppRemoved(mSourceUid); + long elapsedGracePeriodMs = 2 * SECOND_IN_MILLIS; + advanceElapsedClock(elapsedGracePeriodMs); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job3, null, false); + } + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS + elapsedGracePeriodMs, 1)); + assertEquals(expected, + mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(SECOND_IN_MILLIS); + elapsedGracePeriodMs += SECOND_IN_MILLIS; + + // Job starts during grace period and ends after grace period ends + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job4, null); + mQuotaController.prepareForExecutionLocked(job4); + } + final long remainingGracePeriod = gracePeriodMs - elapsedGracePeriodMs; + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + advanceElapsedClock(remainingGracePeriod); + // Wait for handler to update Timer + // Can't directly evaluate the message because for some reason, the captured message returns + // the wrong 'what' even though the correct message goes to the handler and the correct + // path executes. + verify(handler, timeout(gracePeriodMs + 5 * SECOND_IN_MILLIS)).handleMessage(any()); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + expected.add(createTimingSession(start, remainingGracePeriod + 10 * SECOND_IN_MILLIS, 1)); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job4, job4, true); + } + assertEquals(expected, + mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Job starts and runs completely after temp allowlist grace period. + advanceElapsedClock(10 * SECOND_IN_MILLIS); + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job5, null); + mQuotaController.prepareForExecutionLocked(job5); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job5, job5, true); + } + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, + mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + /** * Tests that TOP jobs aren't stopped when an app runs out of quota. */ @Test @@ -4583,6 +4717,116 @@ public class QuotaControllerTest { } /** + * Tests that Timers properly track sessions when an app is added and removed from the temp + * allowlist. + */ + @Test + public void testEJTimerTracking_TempAllowlisting() { + setDischarging(); + setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); + final long gracePeriodMs = 15 * SECOND_IN_MILLIS; + setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, gracePeriodMs); + Handler handler = mQuotaController.getHandler(); + spyOn(handler); + + JobStatus job1 = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting", 1); + JobStatus job2 = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting", 2); + JobStatus job3 = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting", 3); + JobStatus job4 = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting", 4); + JobStatus job5 = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting", 5); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job1, null); + } + assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + List<TimingSession> expected = new ArrayList<>(); + + long start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(job1); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job1, job1, true); + } + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, + mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(SECOND_IN_MILLIS); + + // Job starts after app is added to temp allowlist and stops before removal. + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + mTempAllowlistListener.onAppAdded(mSourceUid); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job2, null); + mQuotaController.prepareForExecutionLocked(job2); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job2, null, false); + } + assertEquals(expected, + mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Job starts after app is added to temp allowlist and stops after removal, + // before grace period ends. + mTempAllowlistListener.onAppAdded(mSourceUid); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job3, null); + mQuotaController.prepareForExecutionLocked(job3); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mTempAllowlistListener.onAppRemoved(mSourceUid); + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + long elapsedGracePeriodMs = 2 * SECOND_IN_MILLIS; + advanceElapsedClock(elapsedGracePeriodMs); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job3, null, false); + } + assertEquals(expected, + mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(SECOND_IN_MILLIS); + elapsedGracePeriodMs += SECOND_IN_MILLIS; + + // Job starts during grace period and ends after grace period ends + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job4, null); + mQuotaController.prepareForExecutionLocked(job4); + } + final long remainingGracePeriod = gracePeriodMs - elapsedGracePeriodMs; + start = JobSchedulerService.sElapsedRealtimeClock.millis() + remainingGracePeriod; + advanceElapsedClock(remainingGracePeriod); + // Wait for handler to update Timer + // Can't directly evaluate the message because for some reason, the captured message returns + // the wrong 'what' even though the correct message goes to the handler and the correct + // path executes. + verify(handler, timeout(gracePeriodMs + 5 * SECOND_IN_MILLIS)).handleMessage(any()); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job4, job4, true); + } + assertEquals(expected, + mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Job starts and runs completely after temp allowlist grace period. + advanceElapsedClock(10 * SECOND_IN_MILLIS); + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job5, null); + mQuotaController.prepareForExecutionLocked(job5); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job5, job5, true); + } + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, + mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + /** * Tests that expedited jobs aren't stopped when an app runs out of quota. */ @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/quota/OWNERS b/services/tests/mockingservicestests/src/com/android/server/utils/quota/OWNERS new file mode 100644 index 000000000000..2e9e62570653 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/utils/quota/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/services/core/java/com/android/server/utils/quota/OWNERS
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java index 726536db859e..0a35db56f35c 100644 --- a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java @@ -24,10 +24,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -48,6 +44,7 @@ import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; +import com.android.server.vibrator.FakeVibratorControllerProvider; import com.android.server.vibrator.VibratorController; import org.junit.After; @@ -81,7 +78,7 @@ public class VibratorManagerServiceTest { @Mock private PowerManagerInternal mPowerManagerInternalMock; @Mock private PowerSaveState mPowerSaveStateMock; - private final Map<Integer, VibratorController.NativeWrapper> mNativeWrappers = new HashMap<>(); + private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>(); private TestLooper mTestLooper; @@ -117,8 +114,8 @@ public class VibratorManagerServiceTest { @Override VibratorController createVibratorController(int vibratorId, VibratorController.OnVibrationCompleteListener listener) { - return new VibratorController( - vibratorId, listener, mNativeWrappers.get(vibratorId)); + return mVibratorProviders.get(vibratorId) + .newVibratorController(vibratorId, listener); } }); service.systemReady(); @@ -126,9 +123,12 @@ public class VibratorManagerServiceTest { } @Test - public void createService_initializesNativeService() { + public void createService_initializesNativeManagerServiceAndVibrators() { + mockVibrators(1, 2); createService(); verify(mNativeWrapperMock).init(); + assertTrue(mVibratorProviders.get(1).isInitialized()); + assertTrue(mVibratorProviders.get(2).isInitialized()); } @Test @@ -139,28 +139,23 @@ public class VibratorManagerServiceTest { @Test public void getVibratorIds_withNonEmptyResultFromNative_returnsSameArray() { - mNativeWrappers.put(1, mockVibrator(/* capabilities= */ 0)); - mNativeWrappers.put(2, mockVibrator(/* capabilities= */ 0)); - when(mNativeWrapperMock.getVibratorIds()).thenReturn(new int[]{2, 1}); + mockVibrators(2, 1); assertArrayEquals(new int[]{2, 1}, createService().getVibratorIds()); } @Test public void getVibratorInfo_withMissingVibratorId_returnsNull() { - mockVibrators(mockVibrator(/* capabilities= */ 0)); + mockVibrators(1); assertNull(createService().getVibratorInfo(2)); } @Test public void getVibratorInfo_withExistingVibratorId_returnsHalInfoForVibrator() { - VibratorController.NativeWrapper vibratorMock = mockVibrator( - IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL); - when(vibratorMock.getSupportedEffects()).thenReturn( - new int[]{VibrationEffect.EFFECT_CLICK}); - when(vibratorMock.getSupportedPrimitives()).thenReturn( - new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}); - mNativeWrappers.put(1, vibratorMock); - when(mNativeWrapperMock.getVibratorIds()).thenReturn(new int[]{1}); + mockVibrators(1); + FakeVibratorControllerProvider vibrator = mVibratorProviders.get(1); + vibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS, IVibrator.CAP_AMPLITUDE_CONTROL); + vibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK); + vibrator.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK); VibratorInfo info = createService().getVibratorInfo(1); assertNotNull(info); @@ -178,105 +173,95 @@ public class VibratorManagerServiceTest { @Test public void setAlwaysOnEffect_withMono_enablesAlwaysOnEffectToAllVibratorsWithCapability() { - VibratorController.NativeWrapper[] vibratorMocks = new VibratorController.NativeWrapper[]{ - mockVibrator(IVibrator.CAP_ALWAYS_ON_CONTROL), - mockVibrator(/* capabilities= */ 0), - mockVibrator(IVibrator.CAP_ALWAYS_ON_CONTROL), - }; - mockVibrators(vibratorMocks); + mockVibrators(1, 2, 3); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL); + mVibratorProviders.get(3).setCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL); CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced( VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); assertTrue(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS)); - // Only vibrators 0 and 2 have always-on capabilities. - verify(vibratorMocks[0]).alwaysOnEnable( - eq(1L), eq((long) VibrationEffect.EFFECT_CLICK), - eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG)); - verify(vibratorMocks[1], never()).alwaysOnEnable(anyLong(), anyLong(), anyLong()); - verify(vibratorMocks[2]).alwaysOnEnable( - eq(1L), eq((long) VibrationEffect.EFFECT_CLICK), - eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG)); + VibrationEffect.Prebaked expectedEffect = new VibrationEffect.Prebaked( + VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG); + + // Only vibrators 1 and 3 have always-on capabilities. + assertEquals(mVibratorProviders.get(1).getAlwaysOnEffect(1), expectedEffect); + assertNull(mVibratorProviders.get(2).getAlwaysOnEffect(1)); + assertEquals(mVibratorProviders.get(3).getAlwaysOnEffect(1), expectedEffect); } @Test public void setAlwaysOnEffect_withStereo_enablesAlwaysOnEffectToAllVibratorsWithCapability() { - VibratorController.NativeWrapper[] vibratorMocks = new VibratorController.NativeWrapper[] { - mockVibrator(IVibrator.CAP_ALWAYS_ON_CONTROL), - mockVibrator(IVibrator.CAP_ALWAYS_ON_CONTROL), - mockVibrator(0), - mockVibrator(IVibrator.CAP_ALWAYS_ON_CONTROL), - }; - mockVibrators(vibratorMocks); + mockVibrators(1, 2, 3, 4); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL); + mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL); + mVibratorProviders.get(4).setCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL); CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced() - .addVibrator(0, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)) - .addVibrator(1, VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK)) - .addVibrator(2, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)) + .addVibrator(1, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)) + .addVibrator(2, VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK)) + .addVibrator(3, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)) .combine(); assertTrue(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS)); - // Enables click on vibrator 0 and tick on vibrator 1 only. - verify(vibratorMocks[0]).alwaysOnEnable( - eq(1L), eq((long) VibrationEffect.EFFECT_CLICK), - eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG)); - verify(vibratorMocks[1]).alwaysOnEnable( - eq(1L), eq((long) VibrationEffect.EFFECT_TICK), - eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG)); - verify(vibratorMocks[2], never()).alwaysOnEnable(anyLong(), anyLong(), anyLong()); - verify(vibratorMocks[3], never()).alwaysOnEnable(anyLong(), anyLong(), anyLong()); + VibrationEffect.Prebaked expectedClick = new VibrationEffect.Prebaked( + VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG); + + VibrationEffect.Prebaked expectedTick = new VibrationEffect.Prebaked( + VibrationEffect.EFFECT_TICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG); + + // Enables click on vibrator 1 and tick on vibrator 2 only. + assertEquals(mVibratorProviders.get(1).getAlwaysOnEffect(1), expectedClick); + assertEquals(mVibratorProviders.get(2).getAlwaysOnEffect(1), expectedTick); + assertNull(mVibratorProviders.get(3).getAlwaysOnEffect(1)); + assertNull(mVibratorProviders.get(4).getAlwaysOnEffect(1)); } @Test public void setAlwaysOnEffect_withNullEffect_disablesAlwaysOnEffects() { - VibratorController.NativeWrapper[] vibratorMocks = new VibratorController.NativeWrapper[] { - mockVibrator(IVibrator.CAP_ALWAYS_ON_CONTROL), - mockVibrator(0), - mockVibrator(IVibrator.CAP_ALWAYS_ON_CONTROL), - }; - mockVibrators(vibratorMocks); + mockVibrators(1, 2, 3); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL); + mVibratorProviders.get(3).setCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL); + + CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced( + VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); + assertTrue(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS)); assertTrue(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, null, ALARM_ATTRS)); - // Disables only 0 and 2 that have capability. - verify(vibratorMocks[0]).alwaysOnDisable(eq(1L)); - verify(vibratorMocks[1], never()).alwaysOnDisable(anyLong()); - verify(vibratorMocks[2]).alwaysOnDisable(eq(1L)); + assertNull(mVibratorProviders.get(1).getAlwaysOnEffect(1)); + assertNull(mVibratorProviders.get(2).getAlwaysOnEffect(1)); + assertNull(mVibratorProviders.get(3).getAlwaysOnEffect(1)); } @Test public void setAlwaysOnEffect_withNonPrebakedEffect_ignoresEffect() { - VibratorController.NativeWrapper vibratorMock = - mockVibrator(IVibrator.CAP_ALWAYS_ON_CONTROL); - mockVibrators(vibratorMock); + mockVibrators(1); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL); CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced( VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); assertFalse(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS)); - verify(vibratorMock, never()).alwaysOnEnable(anyLong(), anyLong(), anyLong()); - verify(vibratorMock, never()).alwaysOnDisable(anyLong()); + assertNull(mVibratorProviders.get(1).getAlwaysOnEffect(1)); } @Test public void setAlwaysOnEffect_withNonSyncedEffect_ignoresEffect() { - VibratorController.NativeWrapper vibratorMock = - mockVibrator(IVibrator.CAP_ALWAYS_ON_CONTROL); - mockVibrators(vibratorMock); + mockVibrators(1); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL); CombinedVibrationEffect effect = CombinedVibrationEffect.startSequential() .addNext(0, VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) .combine(); assertFalse(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS)); - verify(vibratorMock, never()).alwaysOnEnable(anyLong(), anyLong(), anyLong()); - verify(vibratorMock, never()).alwaysOnDisable(anyLong()); + assertNull(mVibratorProviders.get(1).getAlwaysOnEffect(1)); } @Test public void setAlwaysOnEffect_withNoVibratorWithCapability_ignoresEffect() { - VibratorController.NativeWrapper vibratorMock = mockVibrator(0); - mockVibrators(vibratorMock); + mockVibrators(1); VibratorManagerService service = createService(); CombinedVibrationEffect mono = CombinedVibrationEffect.createSynced( @@ -287,8 +272,7 @@ public class VibratorManagerServiceTest { assertFalse(service.setAlwaysOnEffect(UID, PACKAGE_NAME, 1, mono, ALARM_ATTRS)); assertFalse(service.setAlwaysOnEffect(UID, PACKAGE_NAME, 2, stereo, ALARM_ATTRS)); - verify(vibratorMock, never()).alwaysOnEnable(anyLong(), anyLong(), anyLong()); - verify(vibratorMock, never()).alwaysOnDisable(anyLong()); + assertNull(mVibratorProviders.get(1).getAlwaysOnEffect(1)); } @Test @@ -310,19 +294,12 @@ public class VibratorManagerServiceTest { "Not implemented", () -> service.cancelVibrate(service)); } - private VibratorController.NativeWrapper mockVibrator(int capabilities) { - VibratorController.NativeWrapper wrapper = mock(VibratorController.NativeWrapper.class); - when(wrapper.getCapabilities()).thenReturn((long) capabilities); - return wrapper; - } - - private void mockVibrators(VibratorController.NativeWrapper... wrappers) { - int[] ids = new int[wrappers.length]; - for (int i = 0; i < wrappers.length; i++) { - ids[i] = i; - mNativeWrappers.put(i, wrappers[i]); + private void mockVibrators(int... vibratorIds) { + for (int vibratorId : vibratorIds) { + mVibratorProviders.put(vibratorId, + new FakeVibratorControllerProvider(mTestLooper.getLooper())); } - when(mNativeWrapperMock.getVibratorIds()).thenReturn(ids); + when(mNativeWrapperMock.getVibratorIds()).thenReturn(vibratorIds); } private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java index 32ca7b58c48c..5c1e021e8eaf 100644 --- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java @@ -19,21 +19,17 @@ package com.android.server; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.AdditionalMatchers.gt; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.intThat; -import static org.mockito.ArgumentMatchers.notNull; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.never; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.app.AppOpsManager; @@ -54,8 +50,6 @@ import android.os.Looper; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.Process; -import android.os.RemoteException; -import android.os.SystemClock; import android.os.UserHandle; import android.os.VibrationAttributes; import android.os.VibrationEffect; @@ -70,16 +64,16 @@ import androidx.test.InstrumentationRegistry; import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.util.test.FakeSettingsProviderRule; +import com.android.server.vibrator.FakeVibrator; +import com.android.server.vibrator.FakeVibratorControllerProvider; import com.android.server.vibrator.VibratorController; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -87,7 +81,7 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; /** * Tests for {@link VibratorService}. @@ -99,6 +93,7 @@ import java.util.concurrent.atomic.AtomicLong; public class VibratorServiceTest { private static final int UID = Process.ROOT_UID; + private static final int VIBRATOR_ID = 1; private static final String PACKAGE_NAME = "package"; private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build(); private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder() @@ -120,10 +115,7 @@ public class VibratorServiceTest { @Mock private PackageManagerInternal mPackageManagerInternalMock; @Mock private PowerManagerInternal mPowerManagerInternalMock; - // TODO(b/131311651): replace with a FakeVibrator instead. - @Mock private Vibrator mVibratorMock; @Mock private AppOpsManager mAppOpsManagerMock; - @Mock private VibratorController.NativeWrapper mNativeWrapperMock; @Mock private IVibratorStateListener mVibratorStateListenerMock; @Mock private IInputManager mIInputManagerMock; @Mock private IBinder mVibratorStateListenerBinderMock; @@ -131,24 +123,22 @@ public class VibratorServiceTest { private TestLooper mTestLooper; private ContextWrapper mContextSpy; private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener; + private FakeVibrator mFakeVibrator; + private FakeVibratorControllerProvider mVibratorProvider; @Before public void setUp() throws Exception { mTestLooper = new TestLooper(); + mFakeVibrator = new FakeVibrator(); + mVibratorProvider = new FakeVibratorControllerProvider(mTestLooper.getLooper()); mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); InputManager inputManager = InputManager.resetInstance(mIInputManagerMock); ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); - when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibratorMock); + when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mFakeVibrator); when(mContextSpy.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager); when(mContextSpy.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManagerMock); - when(mVibratorMock.getDefaultHapticFeedbackIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM); - when(mVibratorMock.getDefaultNotificationVibrationIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM); - when(mVibratorMock.getDefaultRingVibrationIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM); when(mVibratorStateListenerMock.asBinder()).thenReturn(mVibratorStateListenerBinderMock); when(mPackageManagerInternalMock.getSystemUiServiceComponent()) .thenReturn(new ComponentName("", "")); @@ -183,7 +173,7 @@ public class VibratorServiceTest { @Override VibratorController createVibratorController( VibratorController.OnVibrationCompleteListener listener) { - return new VibratorController(0, listener, mNativeWrapperMock); + return mVibratorProvider.newVibratorController(VIBRATOR_ID, listener); } @Override @@ -203,25 +193,23 @@ public class VibratorServiceTest { @Test public void createService_initializesNativeService() { createService(); - verify(mNativeWrapperMock).init(eq(0), notNull()); - verify(mNativeWrapperMock, times(2)).off(); // Called from constructor and onSystemReady + assertTrue(mVibratorProvider.isInitialized()); } @Test public void hasVibrator_withVibratorHalPresent_returnsTrue() { - when(mNativeWrapperMock.isAvailable()).thenReturn(true); assertTrue(createService().hasVibrator()); } @Test public void hasVibrator_withNoVibratorHalPresent_returnsFalse() { - when(mNativeWrapperMock.isAvailable()).thenReturn(false); + mVibratorProvider.disableVibrators(); assertFalse(createService().hasVibrator()); } @Test public void hasAmplitudeControl_withAmplitudeControlSupport_returnsTrue() { - mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + mVibratorProvider.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); assertTrue(createService().hasAmplitudeControl()); } @@ -234,18 +222,17 @@ public class VibratorServiceTest { public void hasAmplitudeControl_withInputDevices_returnsTrue() throws Exception { when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1}); when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1)); - mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + mVibratorProvider.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1); assertTrue(createService().hasAmplitudeControl()); } @Test public void getVibratorInfo_returnsSameInfoFromNative() { - mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL); - when(mNativeWrapperMock.getSupportedEffects()) - .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK}); - when(mNativeWrapperMock.getSupportedPrimitives()) - .thenReturn(new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}); + mVibratorProvider.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS, + IVibrator.CAP_AMPLITUDE_CONTROL); + mVibratorProvider.setSupportedEffects(VibrationEffect.EFFECT_CLICK); + mVibratorProvider.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK); VibratorInfo info = createService().getVibratorInfo(); assertTrue(info.hasAmplitudeControl()); @@ -258,7 +245,7 @@ public class VibratorServiceTest { } @Test - public void vibrate_withRingtone_usesRingtoneSettings() { + public void vibrate_withRingtone_usesRingtoneSettings() throws Exception { setRingerMode(AudioManager.RINGER_MODE_NORMAL); setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0); @@ -266,34 +253,34 @@ public class VibratorServiceTest { setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 1); - vibrate(createService(), VibrationEffect.createOneShot(10, 10), RINGTONE_ATTRS); + vibrateAndWait(createService(), VibrationEffect.createOneShot(10, 10), RINGTONE_ATTRS); setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0); - vibrate(createService(), VibrationEffect.createOneShot(100, 100), RINGTONE_ATTRS); + vibrateAndWait(createService(), VibrationEffect.createOneShot(100, 100), RINGTONE_ATTRS); - InOrder inOrderVerifier = inOrder(mNativeWrapperMock); - inOrderVerifier.verify(mNativeWrapperMock, never()).on(eq(1L), anyLong()); - inOrderVerifier.verify(mNativeWrapperMock).on(eq(10L), anyLong()); - inOrderVerifier.verify(mNativeWrapperMock).on(eq(100L), anyLong()); + List<VibrationEffect> effects = mVibratorProvider.getEffects(); + assertEquals(2, effects.size()); + assertEquals(10, effects.get(0).getDuration()); + assertEquals(100, effects.get(1).getDuration()); } @Test - public void vibrate_withPowerModeChange_usesLowPowerModeState() { + public void vibrate_withPowerModeChange_usesLowPowerModeState() throws Exception { VibratorService service = createService(); mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE); vibrate(service, VibrationEffect.createOneShot(1, 1), HAPTIC_FEEDBACK_ATTRS); - vibrate(service, VibrationEffect.createOneShot(2, 2), RINGTONE_ATTRS); + vibrateAndWait(service, VibrationEffect.createOneShot(2, 2), RINGTONE_ATTRS); mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE); - vibrate(service, VibrationEffect.createOneShot(3, 3), /* attributes= */ null); - vibrate(service, VibrationEffect.createOneShot(4, 4), NOTIFICATION_ATTRS); + vibrateAndWait(service, VibrationEffect.createOneShot(3, 3), /* attributes= */ null); + vibrateAndWait(service, VibrationEffect.createOneShot(4, 4), NOTIFICATION_ATTRS); - InOrder inOrderVerifier = inOrder(mNativeWrapperMock); - inOrderVerifier.verify(mNativeWrapperMock, never()).on(eq(1L), anyLong()); - inOrderVerifier.verify(mNativeWrapperMock).on(eq(2L), anyLong()); - inOrderVerifier.verify(mNativeWrapperMock).on(eq(3L), anyLong()); - inOrderVerifier.verify(mNativeWrapperMock).on(eq(4L), anyLong()); + List<VibrationEffect> effects = mVibratorProvider.getEffects(); + assertEquals(3, effects.size()); + assertEquals(2, effects.get(0).getDuration()); + assertEquals(3, effects.get(1).getDuration()); + assertEquals(4, effects.get(2).getDuration()); } @Test @@ -349,55 +336,55 @@ public class VibratorServiceTest { when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1)); setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1); VibratorService service = createService(); - Mockito.clearInvocations(mNativeWrapperMock); VibrationEffect effect = VibrationEffect.createOneShot(100, 128); - vibrate(service, effect); - assertFalse(service.isVibrating()); - + vibrate(service, effect, ALARM_ATTRS); verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any()); - verify(mNativeWrapperMock, never()).on(anyLong(), anyLong()); + + // VibrationThread will start this vibration async, so wait before checking it never played. + Thread.sleep(10); + assertTrue(mVibratorProvider.getEffects().isEmpty()); } @Test - public void vibrate_withOneShotAndAmplitudeControl_turnsVibratorOnAndSetsAmplitude() { - mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + public void vibrate_withOneShotAndAmplitudeControl_turnsVibratorOnAndSetsAmplitude() + throws Exception { + mVibratorProvider.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); VibratorService service = createService(); - Mockito.clearInvocations(mNativeWrapperMock); - vibrate(service, VibrationEffect.createOneShot(100, 128)); - assertTrue(service.isVibrating()); + vibrateAndWait(service, VibrationEffect.createOneShot(100, 128), ALARM_ATTRS); - verify(mNativeWrapperMock).off(); - verify(mNativeWrapperMock).on(eq(100L), gt(0L)); - verify(mNativeWrapperMock).setAmplitude(eq(128)); + List<VibrationEffect> effects = mVibratorProvider.getEffects(); + assertEquals(1, effects.size()); + assertEquals(100, effects.get(0).getDuration()); + assertEquals(Arrays.asList(128), mVibratorProvider.getAmplitudes()); } @Test - public void vibrate_withOneShotAndNoAmplitudeControl_turnsVibratorOnAndIgnoresAmplitude() { + public void vibrate_withOneShotAndNoAmplitudeControl_turnsVibratorOnAndIgnoresAmplitude() + throws Exception { VibratorService service = createService(); - Mockito.clearInvocations(mNativeWrapperMock); + clearInvocations(); - vibrate(service, VibrationEffect.createOneShot(100, 128)); - assertTrue(service.isVibrating()); + vibrateAndWait(service, VibrationEffect.createOneShot(100, 128), ALARM_ATTRS); - verify(mNativeWrapperMock).off(); - verify(mNativeWrapperMock).on(eq(100L), gt(0L)); - verify(mNativeWrapperMock, never()).setAmplitude(anyInt()); + List<VibrationEffect> effects = mVibratorProvider.getEffects(); + assertEquals(1, effects.size()); + assertEquals(100, effects.get(0).getDuration()); + assertTrue(mVibratorProvider.getAmplitudes().isEmpty()); } @Test - public void vibrate_withPrebaked_performsEffect() { - when(mNativeWrapperMock.getSupportedEffects()) - .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK}); + public void vibrate_withPrebaked_performsEffect() throws Exception { + mVibratorProvider.setSupportedEffects(VibrationEffect.EFFECT_CLICK); VibratorService service = createService(); - Mockito.clearInvocations(mNativeWrapperMock); - vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); + VibrationEffect effect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK); + vibrateAndWait(service, effect, ALARM_ATTRS); - verify(mNativeWrapperMock).off(); - verify(mNativeWrapperMock).perform(eq((long) VibrationEffect.EFFECT_CLICK), - eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), gt(0L)); + VibrationEffect.Prebaked expectedEffect = new VibrationEffect.Prebaked( + VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG); + assertEquals(Arrays.asList(expectedEffect), mVibratorProvider.getEffects()); } @Test @@ -407,120 +394,62 @@ public class VibratorServiceTest { when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1)); setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1); VibratorService service = createService(); - Mockito.clearInvocations(mNativeWrapperMock); - vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK)); - assertFalse(service.isVibrating()); - - // Wait for VibrateThread to turn input device vibrator ON. - Thread.sleep(5); + vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS); verify(mIInputManagerMock).vibrate(eq(1), any(), any()); - verify(mNativeWrapperMock, never()).on(anyLong(), anyLong()); - verify(mNativeWrapperMock, never()).perform(anyLong(), anyLong(), anyLong()); + + // VibrationThread will start this vibration async, so wait before checking it never played. + Thread.sleep(10); + assertTrue(mVibratorProvider.getEffects().isEmpty()); } @Test - public void vibrate_withComposed_performsEffect() { - mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + public void vibrate_withComposed_performsEffect() throws Exception { + mVibratorProvider.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); VibratorService service = createService(); - Mockito.clearInvocations(mNativeWrapperMock); VibrationEffect effect = VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10) .compose(); - vibrate(service, effect); - - ArgumentCaptor<VibrationEffect.Composition.PrimitiveEffect[]> primitivesCaptor = - ArgumentCaptor.forClass(VibrationEffect.Composition.PrimitiveEffect[].class); - - verify(mNativeWrapperMock).off(); - verify(mNativeWrapperMock).compose(primitivesCaptor.capture(), gt(0L)); - - // Check all primitive effect fields are passed down to the HAL. - assertEquals(1, primitivesCaptor.getValue().length); - VibrationEffect.Composition.PrimitiveEffect primitive = primitivesCaptor.getValue()[0]; - assertEquals(VibrationEffect.Composition.PRIMITIVE_CLICK, primitive.id); - assertEquals(0.5f, primitive.scale, /* delta= */ 1e-2); - assertEquals(10, primitive.delay); + vibrateAndWait(service, effect, ALARM_ATTRS); + assertEquals(Arrays.asList(effect), mVibratorProvider.getEffects()); } @Test - public void vibrate_withComposedAndInputDevices_vibratesInputDevices() - throws Exception { + public void vibrate_withComposedAndInputDevices_vibratesInputDevices() throws Exception { when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1, 2}); when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1)); when(mIInputManagerMock.getInputDevice(2)).thenReturn(createInputDeviceWithVibrator(2)); setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1); VibratorService service = createService(); - Mockito.clearInvocations(mNativeWrapperMock); VibrationEffect effect = VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10) .compose(); - vibrate(service, effect); - assertFalse(service.isVibrating()); + vibrate(service, effect, ALARM_ATTRS); + InOrder inOrderVerifier = inOrder(mIInputManagerMock); + inOrderVerifier.verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any()); + inOrderVerifier.verify(mIInputManagerMock).vibrate(eq(2), eq(effect), any()); - verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any()); - verify(mIInputManagerMock).vibrate(eq(2), eq(effect), any()); - verify(mNativeWrapperMock, never()).compose(any(), anyLong()); + // VibrationThread will start this vibration async, so wait before checking it never played. + Thread.sleep(10); + assertTrue(mVibratorProvider.getEffects().isEmpty()); } @Test public void vibrate_withWaveform_controlsVibratorAmplitudeDuringTotalVibrationTime() throws Exception { - mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + mVibratorProvider.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); VibratorService service = createService(); - Mockito.clearInvocations(mNativeWrapperMock); VibrationEffect effect = VibrationEffect.createWaveform( new long[]{10, 10, 10}, new int[]{100, 200, 50}, -1); - vibrate(service, effect); - - // Wait for VibrateThread to finish: 10ms 100, 10ms 200, 10ms 50. - Thread.sleep(40); - InOrder inOrderVerifier = inOrder(mNativeWrapperMock); - inOrderVerifier.verify(mNativeWrapperMock).off(); - inOrderVerifier.verify(mNativeWrapperMock).on(eq(30L), anyLong()); - inOrderVerifier.verify(mNativeWrapperMock).setAmplitude(eq(100)); - inOrderVerifier.verify(mNativeWrapperMock).setAmplitude(eq(200)); - inOrderVerifier.verify(mNativeWrapperMock).setAmplitude(eq(50)); - inOrderVerifier.verify(mNativeWrapperMock).off(); - } - - @Test - public void vibrate_withWaveform_totalVibrationTimeRespected() throws Exception { - int totalDuration = 10_000; // 10s - int stepDuration = 25; // 25ms - - // 25% of the first waveform step will be spent on the native on() call. - mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); - doAnswer(invocation -> { - Thread.currentThread().sleep(stepDuration / 4); - return null; - }).when(mNativeWrapperMock).on(anyLong(), anyLong()); - // 25% of each waveform step will be spent on the native setAmplitude() call.. - doAnswer(invocation -> { - Thread.currentThread().sleep(stepDuration / 4); - return null; - }).when(mNativeWrapperMock).setAmplitude(anyInt()); + vibrateAndWait(service, effect, ALARM_ATTRS); - VibratorService service = createService(); - - int stepCount = totalDuration / stepDuration; - long[] timings = new long[stepCount]; - int[] amplitudes = new int[stepCount]; - Arrays.fill(timings, stepDuration); - Arrays.fill(amplitudes, VibrationEffect.DEFAULT_AMPLITUDE); - VibrationEffect effect = VibrationEffect.createWaveform(timings, amplitudes, -1); - - int perceivedDuration = vibrateAndMeasure(service, effect, /* timeoutSecs= */ 15); - int delay = Math.abs(perceivedDuration - totalDuration); - - // Allow some delay for thread scheduling and callback triggering. - int maxDelay = (int) (0.05 * totalDuration); // < 5% of total duration - assertTrue("Waveform with perceived delay of " + delay + "ms," - + " expected less than " + maxDelay + "ms", - delay < maxDelay); + assertEquals(Arrays.asList(100, 200, 50), mVibratorProvider.getAmplitudes()); + assertEquals( + Arrays.asList(VibrationEffect.createOneShot(30, VibrationEffect.DEFAULT_AMPLITUDE)), + mVibratorProvider.getEffects()); } @Test @@ -529,123 +458,52 @@ public class VibratorServiceTest { when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1)); setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1); VibratorService service = createService(); - Mockito.clearInvocations(mNativeWrapperMock); VibrationEffect effect = VibrationEffect.createWaveform( new long[]{10, 10, 10}, new int[]{100, 200, 50}, -1); - vibrate(service, effect); - assertFalse(service.isVibrating()); - - // Wait for VibrateThread to turn input device vibrator ON. - Thread.sleep(5); + vibrate(service, effect, ALARM_ATTRS); verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any()); - verify(mNativeWrapperMock, never()).on(anyLong(), anyLong()); - } - @Test - public void vibrate_withOneShotAndNativeCallbackTriggered_finishesVibration() { - VibratorService service = createService(); - doAnswer(invocation -> { - service.onVibrationComplete(invocation.getArgument(1)); - return null; - }).when(mNativeWrapperMock).on(anyLong(), anyLong()); - Mockito.clearInvocations(mNativeWrapperMock); - - vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); - - InOrder inOrderVerifier = inOrder(mNativeWrapperMock); - inOrderVerifier.verify(mNativeWrapperMock).off(); - inOrderVerifier.verify(mNativeWrapperMock).on(eq(100L), gt(0L)); - inOrderVerifier.verify(mNativeWrapperMock).off(); + // VibrationThread will start this vibration async, so wait before checking it never played. + Thread.sleep(10); + assertTrue(mVibratorProvider.getEffects().isEmpty()); } @Test - public void vibrate_withPrebakedAndNativeCallbackTriggered_finishesVibration() { - when(mNativeWrapperMock.getSupportedEffects()) - .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK}); + public void vibrate_withNativeCallbackTriggered_finishesVibration() throws Exception { + mVibratorProvider.setSupportedEffects(VibrationEffect.EFFECT_CLICK); VibratorService service = createService(); - doAnswer(invocation -> { - service.onVibrationComplete(invocation.getArgument(2)); - return 10_000L; // 10s - }).when(mNativeWrapperMock).perform(anyLong(), anyLong(), anyLong()); - Mockito.clearInvocations(mNativeWrapperMock); - - vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); - InOrder inOrderVerifier = inOrder(mNativeWrapperMock); - inOrderVerifier.verify(mNativeWrapperMock).off(); - inOrderVerifier.verify(mNativeWrapperMock).perform( - eq((long) VibrationEffect.EFFECT_CLICK), - eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), - gt(0L)); - inOrderVerifier.verify(mNativeWrapperMock).off(); - } + vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS); - @Test - public void vibrate_withWaveformAndNativeCallback_callbackIgnoredAndWaveformPlaysCompletely() - throws Exception { - VibratorService service = createService(); - doAnswer(invocation -> { - service.onVibrationComplete(invocation.getArgument(1)); - return null; - }).when(mNativeWrapperMock).on(anyLong(), anyLong()); - Mockito.clearInvocations(mNativeWrapperMock); + // VibrationThread will start this vibration async, so wait before triggering callbacks. + Thread.sleep(10); + assertTrue(service.isVibrating()); - VibrationEffect effect = VibrationEffect.createWaveform(new long[]{1, 3, 1, 2}, -1); - vibrate(service, effect); + // Trigger callbacks from controller. + mTestLooper.moveTimeForward(50); + mTestLooper.dispatchAll(); - // Wait for VibrateThread to finish: 1ms OFF, 3ms ON, 1ms OFF, 2ms ON. - Thread.sleep(15); - InOrder inOrderVerifier = inOrder(mNativeWrapperMock); - inOrderVerifier.verify(mNativeWrapperMock, times(2)).off(); - inOrderVerifier.verify(mNativeWrapperMock).on(eq(3L), anyLong()); - inOrderVerifier.verify(mNativeWrapperMock).off(); - inOrderVerifier.verify(mNativeWrapperMock).on(eq(2L), anyLong()); - inOrderVerifier.verify(mNativeWrapperMock).off(); + // VibrationThread needs some time to react to native callbacks and stop the vibrator. + Thread.sleep(10); + assertFalse(service.isVibrating()); } @Test - public void vibrate_withComposedAndNativeCallbackTriggered_finishesVibration() { - mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + public void cancelVibrate_withDeviceVibrating_callsOff() throws Exception { VibratorService service = createService(); - doAnswer(invocation -> { - service.onVibrationComplete(invocation.getArgument(1)); - return null; - }).when(mNativeWrapperMock).compose(any(), anyLong()); - Mockito.clearInvocations(mNativeWrapperMock); - VibrationEffect effect = VibrationEffect.startComposition() - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10) - .compose(); - vibrate(service, effect); - - InOrder inOrderVerifier = inOrder(mNativeWrapperMock); - inOrderVerifier.verify(mNativeWrapperMock).off(); - inOrderVerifier.verify(mNativeWrapperMock).compose( - any(VibrationEffect.Composition.PrimitiveEffect[].class), gt(0L)); - inOrderVerifier.verify(mNativeWrapperMock).off(); - } + vibrate(service, VibrationEffect.createOneShot(100, 100), ALARM_ATTRS); - @Test - public void cancelVibrate_withDeviceVibrating_callsoff() { - VibratorService service = createService(); - vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); + // VibrationThread will start this vibration async, so wait before checking. + Thread.sleep(10); assertTrue(service.isVibrating()); - Mockito.clearInvocations(mNativeWrapperMock); service.cancelVibrate(service); - assertFalse(service.isVibrating()); - verify(mNativeWrapperMock).off(); - } - - @Test - public void cancelVibrate_withDeviceNotVibrating_ignoresCall() { - VibratorService service = createService(); - Mockito.clearInvocations(mNativeWrapperMock); - service.cancelVibrate(service); + // VibrationThread will stop this vibration async, so wait before checking. + Thread.sleep(10); assertFalse(service.isVibrating()); - verify(mNativeWrapperMock, never()).off(); } @Test @@ -653,12 +511,11 @@ public class VibratorServiceTest { VibratorService service = createService(); service.registerVibratorStateListener(mVibratorStateListenerMock); - vibrate(service, VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE)); - service.cancelVibrate(service); + vibrateAndWait(service, VibrationEffect.createOneShot(100, 100), ALARM_ATTRS); InOrder inOrderVerifier = inOrder(mVibratorStateListenerMock); // First notification done when listener is registered. - inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(false); + inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(false)); inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(true)); inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(false)); inOrderVerifier.verifyNoMoreInteractions(); @@ -671,18 +528,25 @@ public class VibratorServiceTest { service.registerVibratorStateListener(mVibratorStateListenerMock); verify(mVibratorStateListenerMock).onVibrating(false); - vibrate(service, VibrationEffect.createOneShot(5, VibrationEffect.DEFAULT_AMPLITUDE)); - verify(mVibratorStateListenerMock).onVibrating(true); + vibrate(service, VibrationEffect.createOneShot(100, 100), ALARM_ATTRS); + // VibrationThread will start this vibration async, so wait before triggering callbacks. + Thread.sleep(10); service.unregisterVibratorStateListener(mVibratorStateListenerMock); - Mockito.clearInvocations(mVibratorStateListenerMock); + // Trigger callbacks from controller. + mTestLooper.moveTimeForward(150); + mTestLooper.dispatchAll(); - vibrate(service, VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE)); - verifyNoMoreInteractions(mVibratorStateListenerMock); + InOrder inOrderVerifier = inOrder(mVibratorStateListenerMock); + // First notification done when listener is registered. + inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(false)); + inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(true)); + inOrderVerifier.verify(mVibratorStateListenerMock, atLeastOnce()).asBinder(); // unregister + inOrderVerifier.verifyNoMoreInteractions(); } @Test - public void scale_withPrebaked_userIntensitySettingAsEffectStrength() { + public void scale_withPrebaked_userIntensitySettingAsEffectStrength() throws Exception { // Alarm vibration is always VIBRATION_INTENSITY_HIGH. setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_MEDIUM); @@ -690,34 +554,34 @@ public class VibratorServiceTest { Vibrator.VIBRATION_INTENSITY_LOW); setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF); + mVibratorProvider.setSupportedEffects( + VibrationEffect.EFFECT_CLICK, + VibrationEffect.EFFECT_TICK, + VibrationEffect.EFFECT_DOUBLE_CLICK, + VibrationEffect.EFFECT_HEAVY_CLICK); VibratorService service = createService(); - vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK), - ALARM_ATTRS); - vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK), + vibrateAndWait(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS); + vibrateAndWait(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), NOTIFICATION_ATTRS); - vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK), + vibrateAndWait(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), HAPTIC_FEEDBACK_ATTRS); - vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK), - RINGTONE_ATTRS); - - verify(mNativeWrapperMock).perform( - eq((long) VibrationEffect.EFFECT_CLICK), - eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), anyLong()); - verify(mNativeWrapperMock).perform( - eq((long) VibrationEffect.EFFECT_TICK), - eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), anyLong()); - verify(mNativeWrapperMock).perform( - eq((long) VibrationEffect.EFFECT_DOUBLE_CLICK), - eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), anyLong()); - verify(mNativeWrapperMock, never()).perform( - eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), anyLong()); + vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), RINGTONE_ATTRS); + + List<Integer> playedStrengths = mVibratorProvider.getEffects().stream() + .map(VibrationEffect.Prebaked.class::cast) + .map(VibrationEffect.Prebaked::getEffectStrength) + .collect(Collectors.toList()); + assertEquals(Arrays.asList( + VibrationEffect.EFFECT_STRENGTH_STRONG, + VibrationEffect.EFFECT_STRENGTH_MEDIUM, + VibrationEffect.EFFECT_STRENGTH_LIGHT), + playedStrengths); } @Test public void scale_withOneShotAndWaveform_usesScaleLevelOnAmplitude() throws Exception { - when(mVibratorMock.getDefaultNotificationVibrationIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_LOW); + mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW); setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, @@ -725,33 +589,29 @@ public class VibratorServiceTest { setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF); - mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + mVibratorProvider.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); VibratorService service = createService(); - vibrate(service, VibrationEffect.createOneShot(20, 100), ALARM_ATTRS); - vibrate(service, VibrationEffect.createOneShot(20, 100), NOTIFICATION_ATTRS); - vibrate(service, VibrationEffect.createOneShot(20, 255), RINGTONE_ATTRS); - vibrate(service, VibrationEffect.createWaveform(new long[] { 10 }, new int[] { 100 }, -1), + vibrateAndWait(service, VibrationEffect.createOneShot(20, 100), ALARM_ATTRS); + vibrateAndWait(service, VibrationEffect.createOneShot(20, 100), NOTIFICATION_ATTRS); + vibrateAndWait(service, + VibrationEffect.createWaveform(new long[]{10}, new int[]{100}, -1), HAPTIC_FEEDBACK_ATTRS); + vibrate(service, VibrationEffect.createOneShot(20, 255), RINGTONE_ATTRS); - // Waveform effect runs on a separate thread. - Thread.sleep(15); - + List<Integer> amplitudes = mVibratorProvider.getAmplitudes(); + assertEquals(3, amplitudes.size()); // Alarm vibration is never scaled. - verify(mNativeWrapperMock).setAmplitude(eq(100)); + assertEquals(100, amplitudes.get(0).intValue()); // Notification vibrations will be scaled with SCALE_VERY_HIGH. - verify(mNativeWrapperMock).setAmplitude(intThat(amplitude -> amplitude > 150)); + assertTrue(amplitudes.get(1) > 150); // Haptic feedback vibrations will be scaled with SCALE_LOW. - verify(mNativeWrapperMock).setAmplitude( - intThat(amplitude -> amplitude < 100 && amplitude > 50)); - // Ringtone vibration is off. - verify(mNativeWrapperMock, never()).setAmplitude(eq(255)); + assertTrue(amplitudes.get(2) < 100 && amplitudes.get(2) > 50); } @Test - public void scale_withComposed_usesScaleLevelOnPrimitiveScaleValues() { - when(mVibratorMock.getDefaultNotificationVibrationIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_LOW); + public void scale_withComposed_usesScaleLevelOnPrimitiveScaleValues() throws Exception { + mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW); setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, @@ -759,82 +619,72 @@ public class VibratorServiceTest { setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF); - mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + mVibratorProvider.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); VibratorService service = createService(); VibrationEffect effect = VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f) .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f) .compose(); - ArgumentCaptor<VibrationEffect.Composition.PrimitiveEffect[]> primitivesCaptor = - ArgumentCaptor.forClass(VibrationEffect.Composition.PrimitiveEffect[].class); - vibrate(service, effect, ALARM_ATTRS); - vibrate(service, effect, NOTIFICATION_ATTRS); - vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS); + vibrateAndWait(service, effect, ALARM_ATTRS); + vibrateAndWait(service, effect, NOTIFICATION_ATTRS); + vibrateAndWait(service, effect, HAPTIC_FEEDBACK_ATTRS); vibrate(service, effect, RINGTONE_ATTRS); - // Ringtone vibration is off, so only the other 3 are propagated to native. - verify(mNativeWrapperMock, times(3)).compose( - primitivesCaptor.capture(), anyLong()); + List<VibrationEffect.Composition.PrimitiveEffect> primitives = + mVibratorProvider.getEffects().stream() + .map(VibrationEffect.Composed.class::cast) + .map(VibrationEffect.Composed::getPrimitiveEffects) + .flatMap(List::stream) + .collect(Collectors.toList()); - List<VibrationEffect.Composition.PrimitiveEffect[]> values = - primitivesCaptor.getAllValues(); + // Ringtone vibration is off, so only the other 3 are propagated to native. + assertEquals(6, primitives.size()); // Alarm vibration is never scaled. - assertEquals(1f, values.get(0)[0].scale, /* delta= */ 1e-2); - assertEquals(0.5f, values.get(0)[1].scale, /* delta= */ 1e-2); + assertEquals(1f, primitives.get(0).scale, /* delta= */ 1e-2); + assertEquals(0.5f, primitives.get(1).scale, /* delta= */ 1e-2); // Notification vibrations will be scaled with SCALE_VERY_HIGH. - assertEquals(1f, values.get(1)[0].scale, /* delta= */ 1e-2); - assertTrue(0.7 < values.get(1)[1].scale); + assertEquals(1f, primitives.get(2).scale, /* delta= */ 1e-2); + assertTrue(0.7 < primitives.get(3).scale); // Haptic feedback vibrations will be scaled with SCALE_LOW. - assertTrue(0.5 < values.get(2)[0].scale); - assertTrue(0.5 > values.get(2)[1].scale); - } - - private void vibrate(VibratorService service, VibrationEffect effect) { - vibrate(service, effect, ALARM_ATTRS); + assertTrue(0.5 < primitives.get(4).scale); + assertTrue(0.5 > primitives.get(5).scale); } private void vibrate(VibratorService service, VibrationEffect effect, - VibrationAttributes attributes) { - service.vibrate(UID, PACKAGE_NAME, effect, attributes, "some reason", service); + VibrationAttributes attrs) { + service.vibrate(UID, PACKAGE_NAME, effect, attrs, "some reason", service); } - private int vibrateAndMeasure( - VibratorService service, VibrationEffect effect, long timeoutSecs) throws Exception { - AtomicLong startTime = new AtomicLong(0); - AtomicLong endTime = new AtomicLong(0); + private void vibrateAndWait(VibratorService service, VibrationEffect effect, + VibrationAttributes attrs) throws Exception { CountDownLatch startedCount = new CountDownLatch(1); CountDownLatch finishedCount = new CountDownLatch(1); service.registerVibratorStateListener(new IVibratorStateListener() { @Override - public void onVibrating(boolean vibrating) throws RemoteException { + public void onVibrating(boolean vibrating) { if (vibrating) { - startTime.set(SystemClock.uptimeMillis()); startedCount.countDown(); } else if (startedCount.getCount() == 0) { - endTime.set(SystemClock.uptimeMillis()); finishedCount.countDown(); } } @Override public IBinder asBinder() { - return mVibratorStateListenerBinderMock; + return mock(IBinder.class); } }); - vibrate(service, effect); - - assertTrue(finishedCount.await(timeoutSecs, TimeUnit.SECONDS)); - return (int) (endTime.get() - startTime.get()); - } - - private void mockVibratorCapabilities(int capabilities) { - when(mNativeWrapperMock.getCapabilities()).thenReturn((long) capabilities); + mTestLooper.startAutoDispatch(); + service.vibrate(UID, PACKAGE_NAME, effect, attrs, "some reason", service); + assertTrue(startedCount.await(1, TimeUnit.SECONDS)); + assertTrue(finishedCount.await(1, TimeUnit.SECONDS)); + mTestLooper.stopAutoDispatchAndIgnoreExceptions(); } private InputDevice createInputDeviceWithVibrator(int id) { diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index 74c6a7e36c9d..784718b5f51e 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -53,7 +53,9 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.validateMockitoUsage; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.testng.Assert.assertThrows; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.IUserSwitchObserver; @@ -71,6 +73,7 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.UserHandle; +import android.os.UserManager; import android.os.storage.IStorageManager; import android.platform.test.annotations.Presubmit; import android.util.Log; @@ -161,7 +164,7 @@ public class UserControllerTest { // All UserController params are set to default. mUserController = new UserController(mInjector); setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS); - setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated=*/ true); + setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated=*/ true, null); }); } @@ -549,19 +552,75 @@ public class UserControllerTest { /* keyEvictedCallback= */ mKeyEvictedCallback, /* expectLocking= */ true); } + @Test + public void testStartProfile_fullUserFails() { + setUpUser(TEST_USER_ID1, 0); + assertThrows(IllegalArgumentException.class, + () -> mUserController.startProfile(TEST_USER_ID1)); + } + + @Test + public void testStopProfile_fullUserFails() throws Exception { + setUpAndStartUserInBackground(TEST_USER_ID1); + assertThrows(IllegalArgumentException.class, + () -> mUserController.stopProfile(TEST_USER_ID1)); + } + + @Test + public void testStartProfile_disabledProfileFails() { + setUpUser(TEST_USER_ID1, UserInfo.FLAG_PROFILE | UserInfo.FLAG_DISABLED, /* preCreated= */ + false, UserManager.USER_TYPE_PROFILE_MANAGED); + assertThat(mUserController.startProfile(TEST_USER_ID1)).isFalse(); + } + + @Test + public void testStartProfile() throws Exception { + setUpAndStartProfileInBackground(TEST_USER_ID1); + startBackgroundUserAssertions(); + } + + @Test + public void testStopProfile() throws Exception { + setUpAndStartProfileInBackground(TEST_USER_ID1); + assertProfileLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* expectLocking= */ true); + } + private void setUpAndStartUserInBackground(int userId) throws Exception { setUpUser(userId, 0); mUserController.startUser(userId, /* foreground= */ false); verify(mInjector.mStorageManagerMock, times(1)) - .unlockUserKey(TEST_USER_ID, 0, null, null); + .unlockUserKey(userId, /* serialNumber= */ 0, /* token= */ null, /* secret= */ + null); + mUserStates.put(userId, mUserController.getStartedUserState(userId)); + } + + private void setUpAndStartProfileInBackground(int userId) throws Exception { + setUpUser(userId, UserInfo.FLAG_PROFILE, false, UserManager.USER_TYPE_PROFILE_MANAGED); + assertThat(mUserController.startProfile(userId)).isTrue(); + + verify(mInjector.mStorageManagerMock, times(1)) + .unlockUserKey(userId, /* serialNumber= */ 0, /* token= */ null, /* secret= */ + null); mUserStates.put(userId, mUserController.getStartedUserState(userId)); } private void assertUserLockedOrUnlockedAfterStopping(int userId, boolean delayedLocking, - KeyEvictedCallback keyEvictedCallback, boolean expectLocking) throws Exception { + KeyEvictedCallback keyEvictedCallback, boolean expectLocking) throws Exception { int r = mUserController.stopUser(userId, /* force= */ true, /* delayedLocking= */ delayedLocking, null, keyEvictedCallback); assertThat(r).isEqualTo(ActivityManager.USER_OP_SUCCESS); + assertUserLockedOrUnlockedState(userId, delayedLocking, expectLocking); + } + + private void assertProfileLockedOrUnlockedAfterStopping(int userId, boolean expectLocking) + throws Exception { + boolean profileStopped = mUserController.stopProfile(userId); + assertThat(profileStopped).isTrue(); + assertUserLockedOrUnlockedState(userId, /* delayedLocking= */ false, expectLocking); + } + + private void assertUserLockedOrUnlockedState(int userId, boolean delayedLocking, + boolean expectLocking) throws InterruptedException, RemoteException { // fake all interim steps UserState ussUser = mUserStates.get(userId); ussUser.setState(UserState.STATE_SHUTDOWN); @@ -594,11 +653,16 @@ public class UserControllerTest { } private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags) { - setUpUser(userId, flags, /* preCreated= */ false); + setUpUser(userId, flags, /* preCreated= */ false, /* userType */ null); } - private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags, boolean preCreated) { - UserInfo userInfo = new UserInfo(userId, "User" + userId, flags); + private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags, boolean preCreated, + @Nullable String userType) { + if (userType == null) { + userType = UserInfo.getDefaultUserType(flags); + } + UserInfo userInfo = new UserInfo(userId, "User" + userId, /* iconPath= */ null, flags, + userType); userInfo.preCreated = preCreated; when(mInjector.mUserManagerMock.getUserInfo(eq(userId))).thenReturn(userInfo); when(mInjector.mUserManagerMock.isPreCreated(userId)).thenReturn(preCreated); 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 009cb0b363e4..6366155a4a39 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 @@ -1230,14 +1230,14 @@ public class AppSearchImplTest { @Test public void testRewriteSearchResultProto() throws Exception { - final String database = + final String prefix = "com.package.foo" + AppSearchImpl.PACKAGE_DELIMITER + "databaseName" + AppSearchImpl.DATABASE_DELIMITER; final String uri = "uri"; - final String namespace = database + "namespace"; - final String schemaType = database + "schema"; + final String namespace = prefix + "namespace"; + final String schemaType = prefix + "schema"; // Building the SearchResult received from query. DocumentProto documentProto = @@ -1257,6 +1257,7 @@ public class AppSearchImplTest { AppSearchImpl.rewriteSearchResultProto(searchResultProto); for (SearchResult result : searchResultPage.getResults()) { assertThat(result.getPackageName()).isEqualTo("com.package.foo"); + assertThat(result.getDatabaseName()).isEqualTo("databaseName"); assertThat(result.getDocument()) .isEqualTo( GenericDocumentToProtoConverter.toGenericDocument( diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java index a3f0f6bf280e..97daea3011ea 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java @@ -86,7 +86,9 @@ public class SnippetTest { // Making ResultReader and getting Snippet values. SearchResultPage searchResultPage = SearchResultToProtoConverter.toSearchResultPage( - searchResultProto, Collections.singletonList("packageName")); + searchResultProto, + Collections.singletonList("packageName"), + Collections.singletonList("databaseName")); for (SearchResult result : searchResultPage.getResults()) { SearchResult.MatchInfo match = result.getMatches().get(0); assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString); @@ -135,7 +137,9 @@ public class SnippetTest { SearchResultPage searchResultPage = SearchResultToProtoConverter.toSearchResultPage( - searchResultProto, Collections.singletonList("packageName")); + searchResultProto, + Collections.singletonList("packageName"), + Collections.singletonList("databaseName")); for (SearchResult result : searchResultPage.getResults()) { assertThat(result.getMatches()).isEmpty(); } @@ -201,7 +205,9 @@ public class SnippetTest { // Making ResultReader and getting Snippet values. SearchResultPage searchResultPage = SearchResultToProtoConverter.toSearchResultPage( - searchResultProto, Collections.singletonList("packageName")); + searchResultProto, + Collections.singletonList("packageName"), + Collections.singletonList("databaseName")); for (SearchResult result : searchResultPage.getResults()) { SearchResult.MatchInfo match1 = result.getMatches().get(0); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/InvalidationTrackerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/InvalidationTrackerTest.java index 340a1d9bdda0..bb2b1c2fb0db 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/InvalidationTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/InvalidationTrackerTest.java @@ -22,7 +22,9 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricManager.Authenticators; import android.hardware.biometrics.IBiometricAuthenticator; @@ -44,21 +46,25 @@ public class InvalidationTrackerTest { @Test public void testCallbackReceived_whenAllStrongSensorsInvalidated() throws Exception { final IBiometricAuthenticator authenticator1 = mock(IBiometricAuthenticator.class); + when(authenticator1.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); final TestSensor sensor1 = new TestSensor(0 /* id */, BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG, authenticator1); final IBiometricAuthenticator authenticator2 = mock(IBiometricAuthenticator.class); + when(authenticator2.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); final TestSensor sensor2 = new TestSensor(1 /* id */, BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG, authenticator2); final IBiometricAuthenticator authenticator3 = mock(IBiometricAuthenticator.class); + when(authenticator3.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); final TestSensor sensor3 = new TestSensor(2 /* id */, BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG, authenticator3); final IBiometricAuthenticator authenticator4 = mock(IBiometricAuthenticator.class); + when(authenticator4.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); final TestSensor sensor4 = new TestSensor(3 /* id */, BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_WEAK, authenticator4); @@ -71,7 +77,8 @@ public class InvalidationTrackerTest { final IInvalidationCallback callback = mock(IInvalidationCallback.class); final InvalidationTracker tracker = - InvalidationTracker.start(sensors, 0 /* userId */, 0 /* fromSensorId */, callback); + InvalidationTracker.start(mock(Context.class), sensors, 0 /* userId */, + 0 /* fromSensorId */, callback); // The sensor which the request originated from should not be requested to invalidate // its authenticatorId. diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index 600f6815d322..cc4541b438e0 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -71,11 +71,11 @@ public class BiometricSchedulerTest { @Test public void testClientDuplicateFinish_ignoredBySchedulerAndDoesNotCrash() { - final BaseClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class); + final HalClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class); - final BaseClientMonitor<Object> client1 = + final HalClientMonitor<Object> client1 = new TestClientMonitor(mContext, mToken, nonNullDaemon); - final BaseClientMonitor<Object> client2 = + final HalClientMonitor<Object> client2 = new TestClientMonitor(mContext, mToken, nonNullDaemon); mScheduler.scheduleClientMonitor(client1); mScheduler.scheduleClientMonitor(client2); @@ -89,8 +89,8 @@ public class BiometricSchedulerTest { // Even if second client has a non-null daemon, it needs to be canceled. Object daemon2 = mock(Object.class); - final BaseClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null; - final BaseClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2; + final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null; + final HalClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2; final TestClientMonitor client1 = new TestClientMonitor(mContext, mToken, lazyDaemon1); final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2); @@ -126,8 +126,8 @@ public class BiometricSchedulerTest { // Second non-BiometricPrompt client has a valid daemon final Object daemon2 = mock(Object.class); - final BaseClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null; - final BaseClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2; + final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null; + final HalClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2; final ClientMonitorCallbackConverter listener1 = mock(ClientMonitorCallbackConverter.class); @@ -167,7 +167,7 @@ public class BiometricSchedulerTest { @Test public void testCancelNotInvoked_whenOperationWaitingForCookie() { - final BaseClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> mock(Object.class); + final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> mock(Object.class); final BiometricPromptClientMonitor client1 = new BiometricPromptClientMonitor(mContext, mToken, lazyDaemon1, mock(ClientMonitorCallbackConverter.class)); final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class); @@ -207,7 +207,7 @@ public class BiometricSchedulerTest { } } - private static class TestClientMonitor extends BaseClientMonitor<Object> { + private static class TestClientMonitor extends HalClientMonitor<Object> { private boolean mUnableToStart; private boolean mStarted; @@ -223,7 +223,6 @@ public class BiometricSchedulerTest { 0 /* statsAction */, 0 /* statsClient */); } - @Override public void unableToStart() { assertFalse(mUnableToStart); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java index ffd43780bfbe..04a7122ba426 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java @@ -33,6 +33,7 @@ import androidx.test.filters.SmallTest; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricScheduler; +import com.android.server.biometrics.sensors.HalClientMonitor; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import org.junit.Before; @@ -94,7 +95,7 @@ public class FaceProviderTest { final BiometricScheduler scheduler = mFaceProvider.mSensors.get(prop.commonProps.sensorId).getScheduler(); for (int i = 0; i < numFakeOperations; i++) { - final BaseClientMonitor testMonitor = mock(BaseClientMonitor.class); + final HalClientMonitor testMonitor = mock(HalClientMonitor.class); when(testMonitor.getFreshDaemon()).thenReturn(new Object()); scheduler.scheduleClientMonitor(testMonitor); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java index 99aab5c7a6af..392535e8eea1 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors.face.hidl; +import static junit.framework.Assert.assertEquals; + import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -76,6 +78,12 @@ public class Face10Test { } @Test + public void getAuthenticatorId_doesNotCrashWhenIdNotFound() { + assertEquals(0, mFace10.getAuthenticatorId(0 /* sensorId */, 111 /* userId */)); + waitForIdle(); + } + + @Test public void scheduleRevokeChallenge_doesNotCrash() { mFace10.scheduleRevokeChallenge(0 /* sensorId */, 0 /* userId */, mBinder, TAG, 0 /* challenge */); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java index 175c4dacfa88..d149880e5505 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java @@ -33,6 +33,7 @@ import androidx.test.filters.SmallTest; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricScheduler; +import com.android.server.biometrics.sensors.HalClientMonitor; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; @@ -97,7 +98,7 @@ public class FingerprintProviderTest { final BiometricScheduler scheduler = mFingerprintProvider.mSensors.get(prop.commonProps.sensorId).getScheduler(); for (int i = 0; i < numFakeOperations; i++) { - final BaseClientMonitor testMonitor = mock(BaseClientMonitor.class); + final HalClientMonitor testMonitor = mock(HalClientMonitor.class); when(testMonitor.getFreshDaemon()).thenReturn(new Object()); scheduler.scheduleClientMonitor(testMonitor); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java index b2aeb33039f5..61cc8e6e8ea3 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors.fingerprint.hidl; +import static junit.framework.Assert.assertEquals; + import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -85,6 +87,12 @@ public class Fingerprint21Test { } @Test + public void getAuthenticatorId_doesNotCrashWhenIdNotFound() { + assertEquals(0, mFingerprint21.getAuthenticatorId(0 /* sensorId */, 111 /* userId */)); + waitForIdle(); + } + + @Test public void halServiceDied_resetsScheduler() { // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke // serviceDied directly. diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java new file mode 100644 index 000000000000..0cf0af39dc1e --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.graphics.fonts; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import android.content.Context; +import android.os.FileUtils; +import android.platform.test.annotations.Presubmit; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Map; + +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +public final class UpdatableFontDirTest { + + /** + * A {@link UpdatableFontDir.FontFileParser} for testing. Instead of using real font files, + * this test uses fake font files. A fake font file has its version as its file content. + */ + private static class FakeFontFileParser implements UpdatableFontDir.FontFileParser { + @Override + public long getVersion(File file) throws IOException { + return Long.parseLong(FileUtils.readTextFile(file, 100, "")); + } + } + + private File mCacheDir; + private File mUpdatableFontFilesDir; + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Before + public void setUp() { + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + mCacheDir = new File(context.getCacheDir(), "UpdatableFontDirTest"); + FileUtils.deleteContentsAndDir(mCacheDir); + mCacheDir.mkdirs(); + mUpdatableFontFilesDir = new File(mCacheDir, "updatable_fonts"); + mUpdatableFontFilesDir.mkdir(); + } + + @After + public void tearDown() { + FileUtils.deleteContentsAndDir(mCacheDir); + } + + @Test + public void construct() throws Exception { + FakeFontFileParser parser = new FakeFontFileParser(); + UpdatableFontDir dirForPreparation = new UpdatableFontDir(mUpdatableFontFilesDir, parser); + installFontFile(dirForPreparation, "foo.ttf", "1"); + installFontFile(dirForPreparation, "bar.ttf", "2"); + installFontFile(dirForPreparation, "foo.ttf", "3"); + installFontFile(dirForPreparation, "bar.ttf", "4"); + // Four font dirs are created. + assertThat(mUpdatableFontFilesDir.list()).hasLength(4); + + UpdatableFontDir dir = new UpdatableFontDir(mUpdatableFontFilesDir, parser); + assertThat(dir.getFontFileMap()).containsKey("foo.ttf"); + assertThat(parser.getVersion(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(3); + assertThat(dir.getFontFileMap()).containsKey("bar.ttf"); + assertThat(parser.getVersion(dir.getFontFileMap().get("bar.ttf"))).isEqualTo(4); + // Outdated font dir should be deleted. + assertThat(mUpdatableFontFilesDir.list()).hasLength(2); + } + + @Test + public void construct_empty() { + FakeFontFileParser parser = new FakeFontFileParser(); + UpdatableFontDir dir = new UpdatableFontDir(mUpdatableFontFilesDir, parser); + assertThat(dir.getFontFileMap()).isEmpty(); + } + + @Test + public void installFontFile() throws Exception { + FakeFontFileParser parser = new FakeFontFileParser(); + UpdatableFontDir dir = new UpdatableFontDir(mUpdatableFontFilesDir, parser); + + installFontFile(dir, "test.ttf", "1"); + assertThat(dir.getFontFileMap()).containsKey("test.ttf"); + assertThat(parser.getVersion(dir.getFontFileMap().get("test.ttf"))).isEqualTo(1); + } + + @Test + public void installFontFile_upgrade() throws Exception { + FakeFontFileParser parser = new FakeFontFileParser(); + UpdatableFontDir dir = new UpdatableFontDir(mUpdatableFontFilesDir, parser); + + installFontFile(dir, "test.ttf", "1"); + Map<String, File> mapBeforeUpgrade = dir.getFontFileMap(); + installFontFile(dir, "test.ttf", "2"); + assertThat(dir.getFontFileMap()).containsKey("test.ttf"); + assertThat(parser.getVersion(dir.getFontFileMap().get("test.ttf"))).isEqualTo(2); + assertThat(mapBeforeUpgrade).containsKey("test.ttf"); + assertWithMessage("Older fonts should not be deleted until next loadFontFileMap") + .that(parser.getVersion(mapBeforeUpgrade.get("test.ttf"))).isEqualTo(1); + } + + @Test + public void installFontFile_downgrade() throws Exception { + FakeFontFileParser parser = new FakeFontFileParser(); + UpdatableFontDir dir = new UpdatableFontDir(mUpdatableFontFilesDir, parser); + + installFontFile(dir, "test.ttf", "2"); + installFontFile(dir, "test.ttf", "1"); + assertThat(dir.getFontFileMap()).containsKey("test.ttf"); + assertWithMessage("Font should not be downgraded to an older version") + .that(parser.getVersion(dir.getFontFileMap().get("test.ttf"))).isEqualTo(2); + } + + @Test + public void installFontFile_multiple() throws Exception { + FakeFontFileParser parser = new FakeFontFileParser(); + UpdatableFontDir dir = new UpdatableFontDir(mUpdatableFontFilesDir, parser); + + installFontFile(dir, "foo.ttf", "1"); + installFontFile(dir, "bar.ttf", "2"); + assertThat(dir.getFontFileMap()).containsKey("foo.ttf"); + assertThat(parser.getVersion(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1); + assertThat(dir.getFontFileMap()).containsKey("bar.ttf"); + assertThat(parser.getVersion(dir.getFontFileMap().get("bar.ttf"))).isEqualTo(2); + } + + private void installFontFile(UpdatableFontDir dir, String name, String content) + throws IOException { + File file = File.createTempFile(name, "", mCacheDir); + FileUtils.stringToFile(file, content); + try (FileInputStream in = new FileInputStream(file)) { + dir.installFontFile(name, in.getFD()); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java index b7d9a56c9c76..a7b32ac5c387 100644 --- a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java +++ b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java @@ -41,6 +41,7 @@ import android.provider.Settings; import android.util.Log; import androidx.test.InstrumentationRegistry; +import androidx.test.filters.FlakyTest; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; @@ -134,6 +135,7 @@ public class BackgroundRestrictionsTest { assertTrue("Job did not start after scheduling", awaitJobStart(DEFAULT_WAIT_TIMEOUT)); } + @FlakyTest @Test public void testPowerExemption() throws Exception { scheduleAndAssertJobStarted(); diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java index 5f654406812f..a5039fb5fecd 100644 --- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java @@ -82,6 +82,7 @@ public class PowerStatsServiceTest { private PowerStatsLogger mPowerStatsLogger; private final PowerStatsService.Injector mInjector = new PowerStatsService.Injector() { + private TestPowerStatsHALWrapper mTestPowerStatsHALWrapper = new TestPowerStatsHALWrapper(); @Override File createDataStoragePath() { mDataStorageDir = null; @@ -111,8 +112,8 @@ public class PowerStatsServiceTest { } @Override - IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() { - return new TestPowerStatsHALWrapper(); + IPowerStatsHALWrapper getPowerStatsHALWrapperImpl() { + return mTestPowerStatsHALWrapper; } @Override diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java index 385837009b2f..aadab6ea4fd9 100644 --- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java @@ -25,6 +25,7 @@ import android.content.ContextWrapper; import android.media.tv.ITvInputManager; import android.media.tv.TvInputManager; import android.media.tv.TvInputService; +import android.media.tv.tuner.TunerFrontendInfo; import android.media.tv.tuner.frontend.FrontendSettings; import android.media.tv.tunerresourcemanager.CasSessionRequest; import android.media.tv.tunerresourcemanager.IResourcesReclaimListener; @@ -32,7 +33,6 @@ import android.media.tv.tunerresourcemanager.ResourceClientProfile; import android.media.tv.tunerresourcemanager.TunerCiCamRequest; import android.media.tv.tunerresourcemanager.TunerDemuxRequest; import android.media.tv.tunerresourcemanager.TunerDescramblerRequest; -import android.media.tv.tunerresourcemanager.TunerFrontendInfo; import android.media.tv.tunerresourcemanager.TunerFrontendRequest; import android.media.tv.tunerresourcemanager.TunerLnbRequest; import android.media.tv.tunerresourcemanager.TunerResourceManager; @@ -88,7 +88,7 @@ public class TunerResourceManagerServiceTest { } return actual.getHandle() == expected.handle - && actual.getType() == expected.frontendType + && actual.getType() == expected.type && actual.getExclusiveGroupId() == expected.exclusiveGroupId; }, "is correctly configured from "); @@ -1156,7 +1156,7 @@ public class TunerResourceManagerServiceTest { int handle, int frontendType, int exclusiveGroupId) { TunerFrontendInfo info = new TunerFrontendInfo(); info.handle = handle; - info.frontendType = frontendType; + info.type = frontendType; info.exclusiveGroupId = exclusiveGroupId; return info; } diff --git a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java index 7c65dc03a57e..b40d59c14b8d 100644 --- a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java @@ -254,6 +254,8 @@ public class WatcherTest { tester.verify(15, "Tick after snapshot"); // Verify that the snapshot is sealed verifySealed(name, ()->arraySnap.put(INDEX_A, leafA)); + assertTrue(!array.isSealed()); + assertTrue(arraySnap.isSealed()); } // Recreate the snapshot since the test corrupted it. { @@ -355,6 +357,8 @@ public class WatcherTest { tester.verify(16, "Tick after snapshot"); // Verify that the array snapshot is sealed verifySealed(name, ()->arraySnap.add(leafB)); + assertTrue(!array.isSealed()); + assertTrue(arraySnap.isSealed()); } // Recreate the snapshot since the test corrupted it. { @@ -467,6 +471,8 @@ public class WatcherTest { tester.verify(20, "Tick after snapshot"); // Verify that the array snapshot is sealed verifySealed(name, ()->arraySnap.add(indexA, leafB)); + assertTrue(!array.isSealed()); + assertTrue(arraySnap.isSealed()); } // Recreate the snapshot since the test corrupted it. { @@ -586,6 +592,8 @@ public class WatcherTest { tester.verify(23, "Tick after snapshot"); // Verify that the array snapshot is sealed verifySealed(name, ()->arraySnap.put(INDEX_A, leafB)); + assertTrue(!array.isSealed()); + assertTrue(arraySnap.isSealed()); } // Recreate the snapshot since the test corrupted it. { @@ -649,6 +657,8 @@ public class WatcherTest { tester.verify(6, "Tick after snapshot"); // Verify that the array is sealed verifySealed(name, ()->arraySnap.put(INDEX_D, false)); + assertTrue(!array.isSealed()); + assertTrue(arraySnap.isSealed()); } // Verify copy-in/out { @@ -705,6 +715,8 @@ public class WatcherTest { tester.verify(6, "Tick after snapshot"); // Verify that the array is sealed verifySealed(name, ()->arraySnap.put(INDEX_D, 10)); + assertTrue(!array.isSealed()); + assertTrue(arraySnap.isSealed()); } // Verify copy-in/out { diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java new file mode 100644 index 000000000000..72c40ea5a2be --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import android.os.VibrationAttributes; +import android.os.VibrationEffect; +import android.os.Vibrator; + +import androidx.annotation.NonNull; + +/** Fake implementation of {@link Vibrator} for service tests. */ +public final class FakeVibrator extends Vibrator { + + private int mDefaultHapticFeedbackIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM; + private int mDefaultNotificationIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM; + private int mDefaultRingIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM; + + @Override + public int getDefaultHapticFeedbackIntensity() { + return mDefaultHapticFeedbackIntensity; + } + + @Override + public int getDefaultNotificationVibrationIntensity() { + return mDefaultNotificationIntensity; + } + + @Override + public int getDefaultRingVibrationIntensity() { + return mDefaultRingIntensity; + } + + public void setDefaultHapticFeedbackIntensity( + @VibrationIntensity int defaultHapticFeedbackIntensity) { + mDefaultHapticFeedbackIntensity = defaultHapticFeedbackIntensity; + } + + public void setDefaultNotificationVibrationIntensity( + @VibrationIntensity int defaultNotificationIntensity) { + mDefaultNotificationIntensity = defaultNotificationIntensity; + } + + public void setDefaultRingVibrationIntensity(@VibrationIntensity int defaultRingIntensity) { + mDefaultRingIntensity = defaultRingIntensity; + } + + @Override + public boolean hasVibrator() { + return true; + } + + @Override + public boolean hasAmplitudeControl() { + return true; + } + + @Override + public void vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe, String reason, + @NonNull VibrationAttributes attributes) { + } + + @Override + public void cancel() { + } +} diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java new file mode 100644 index 000000000000..f562c1613413 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import android.annotation.Nullable; +import android.os.Handler; +import android.os.Looper; +import android.os.VibrationEffect; + +import com.android.server.vibrator.VibratorController.OnVibrationCompleteListener; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Provides {@link VibratorController} with controlled vibrator hardware capabilities and + * interactions. + */ +public final class FakeVibratorControllerProvider { + + private static final int EFFECT_DURATION = 20; + + private final Map<Long, VibrationEffect.Prebaked> mEnabledAlwaysOnEffects = new HashMap<>(); + private final List<VibrationEffect> mEffects = new ArrayList<>(); + private final List<Integer> mAmplitudes = new ArrayList<>(); + private final Handler mHandler; + private final FakeNativeWrapper mNativeWrapper; + + private boolean mIsAvailable = true; + private long mLatency; + + private int mCapabilities; + private int[] mSupportedEffects; + private int[] mSupportedPrimitives; + + private final class FakeNativeWrapper extends VibratorController.NativeWrapper { + public int vibratorId; + public OnVibrationCompleteListener listener; + public boolean isInitialized; + + public void init(int vibratorId, OnVibrationCompleteListener listener) { + isInitialized = true; + this.vibratorId = vibratorId; + this.listener = listener; + } + + public boolean isAvailable() { + return mIsAvailable; + } + + public void on(long milliseconds, long vibrationId) { + VibrationEffect effect = VibrationEffect.createOneShot( + milliseconds, VibrationEffect.DEFAULT_AMPLITUDE); + mEffects.add(effect); + applyLatency(); + scheduleListener(milliseconds, vibrationId); + } + + public void off() { + } + + public void setAmplitude(int amplitude) { + mAmplitudes.add(amplitude); + applyLatency(); + } + + public int[] getSupportedEffects() { + return mSupportedEffects; + } + + public int[] getSupportedPrimitives() { + return mSupportedPrimitives; + } + + public long perform(long effect, long strength, long vibrationId) { + if (mSupportedEffects == null + || Arrays.binarySearch(mSupportedEffects, (int) effect) < 0) { + return 0; + } + mEffects.add(new VibrationEffect.Prebaked((int) effect, false, (int) strength)); + applyLatency(); + scheduleListener(EFFECT_DURATION, vibrationId); + return EFFECT_DURATION; + } + + public void compose(VibrationEffect.Composition.PrimitiveEffect[] effect, + long vibrationId) { + VibrationEffect.Composed composed = new VibrationEffect.Composed(Arrays.asList(effect)); + mEffects.add(composed); + applyLatency(); + long duration = EFFECT_DURATION * effect.length; + for (VibrationEffect.Composition.PrimitiveEffect e : effect) { + duration += e.delay; + } + scheduleListener(duration, vibrationId); + } + + public void setExternalControl(boolean enabled) { + } + + public long getCapabilities() { + return mCapabilities; + } + + public void alwaysOnEnable(long id, long effect, long strength) { + VibrationEffect.Prebaked prebaked = new VibrationEffect.Prebaked((int) effect, false, + (int) strength); + mEnabledAlwaysOnEffects.put(id, prebaked); + } + + public void alwaysOnDisable(long id) { + mEnabledAlwaysOnEffects.remove(id); + } + + private void applyLatency() { + try { + if (mLatency > 0) { + Thread.sleep(mLatency); + } + } catch (InterruptedException e) { + } + } + + private void scheduleListener(long vibrationDuration, long vibrationId) { + mHandler.postDelayed(() -> listener.onComplete(vibratorId, vibrationId), + vibrationDuration); + } + } + + public FakeVibratorControllerProvider(Looper looper) { + mHandler = new Handler(looper); + mNativeWrapper = new FakeNativeWrapper(); + } + + public VibratorController newVibratorController( + int vibratorId, OnVibrationCompleteListener listener) { + return new VibratorController(vibratorId, listener, mNativeWrapper); + } + + /** Return {@code true} if this controller was initialized. */ + public boolean isInitialized() { + return mNativeWrapper.isInitialized; + } + + /** + * Disable fake vibrator hardware, mocking a state where the underlying service is unavailable. + */ + public void disableVibrators() { + mIsAvailable = false; + } + + /** + * Sets the latency this controller should fake for turning the vibrator hardware on or setting + * it's vibration amplitude. + */ + public void setLatency(long millis) { + mLatency = millis; + } + + /** Set the capabilities of the fake vibrator hardware. */ + public void setCapabilities(int... capabilities) { + mCapabilities = Arrays.stream(capabilities).reduce(0, (a, b) -> a | b); + } + + /** Set the effects supported by the fake vibrator hardware. */ + public void setSupportedEffects(int... effects) { + if (effects != null) { + effects = Arrays.copyOf(effects, effects.length); + Arrays.sort(effects); + } + mSupportedEffects = effects; + } + + /** Set the primitives supported by the fake vibrator hardware. */ + public void setSupportedPrimitives(int... primitives) { + if (primitives != null) { + primitives = Arrays.copyOf(primitives, primitives.length); + Arrays.sort(primitives); + } + mSupportedPrimitives = primitives; + } + + /** + * Return the amplitudes set by this controller, including zeroes for each time the vibrator was + * turned off. + */ + public List<Integer> getAmplitudes() { + return new ArrayList<>(mAmplitudes); + } + + /** Return list of {@link VibrationEffect} played by this controller, in order. */ + public List<VibrationEffect> getEffects() { + return new ArrayList<>(mEffects); + } + + /** + * Return the {@link VibrationEffect.Prebaked} effect enabled with given id, or {@code null} if + * missing or disabled. + */ + @Nullable + public VibrationEffect.Prebaked getAlwaysOnEffect(int id) { + return mEnabledAlwaysOnEffects.get((long) id); + } +} diff --git a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java index ac93ff691925..28d313b4d4b5 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java @@ -34,6 +34,7 @@ import android.content.ContextWrapper; import android.hardware.input.IInputDevicesChangedListener; import android.hardware.input.IInputManager; import android.hardware.input.InputManager; +import android.os.CombinedVibrationEffect; import android.os.Handler; import android.os.Process; import android.os.VibrationAttributes; @@ -66,6 +67,9 @@ public class InputDeviceDelegateTest { private static final String REASON = "some reason"; private static final VibrationAttributes VIBRATION_ATTRIBUTES = new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build(); + private static final VibrationEffect EFFECT = VibrationEffect.createOneShot(100, 255); + private static final CombinedVibrationEffect SYNCED_EFFECT = + CombinedVibrationEffect.createSynced(EFFECT); @Rule public MockitoRule rule = MockitoJUnit.rule(); @@ -227,10 +231,9 @@ public class InputDeviceDelegateTest { @Test public void vibrateIfAvailable_withNoInputDevice_returnsFalse() { - VibrationEffect effect = VibrationEffect.createOneShot(100, 255); assertFalse(mInputDeviceDelegate.isAvailable()); assertFalse(mInputDeviceDelegate.vibrateIfAvailable( - UID, PACKAGE_NAME, effect, REASON, VIBRATION_ATTRIBUTES)); + UID, PACKAGE_NAME, SYNCED_EFFECT, REASON, VIBRATION_ATTRIBUTES)); } @Test @@ -241,11 +244,10 @@ public class InputDeviceDelegateTest { when(mIInputManagerMock.getInputDevice(eq(2))).thenReturn(createInputDeviceWithVibrator(2)); mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true); - VibrationEffect effect = VibrationEffect.createOneShot(100, 255); assertTrue(mInputDeviceDelegate.vibrateIfAvailable( - UID, PACKAGE_NAME, effect, REASON, VIBRATION_ATTRIBUTES)); - verify(mIInputManagerMock).vibrate(eq(1), same(effect), any()); - verify(mIInputManagerMock).vibrate(eq(2), same(effect), any()); + UID, PACKAGE_NAME, SYNCED_EFFECT, REASON, VIBRATION_ATTRIBUTES)); + verify(mIInputManagerMock).vibrate(eq(1), same(EFFECT), any()); + verify(mIInputManagerMock).vibrate(eq(2), same(EFFECT), any()); } @Test diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java index 1a4ac0777ba5..1e6ef9137686 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.when; import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; +import android.os.CombinedVibrationEffect; import android.os.Handler; import android.os.IExternalVibratorService; import android.os.PowerManagerInternal; @@ -63,23 +64,23 @@ public class VibrationScalerTest { @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); - // TODO(b/131311651): replace with a FakeVibrator instead. - @Mock private Vibrator mVibratorMock; @Mock private PowerManagerInternal mPowerManagerInternalMock; private TestLooper mTestLooper; private ContextWrapper mContextSpy; + private FakeVibrator mFakeVibrator; private VibrationSettings mVibrationSettings; private VibrationScaler mVibrationScaler; @Before public void setUp() throws Exception { mTestLooper = new TestLooper(); + mFakeVibrator = new FakeVibrator(); mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); - when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibratorMock); + when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mFakeVibrator); LocalServices.removeServiceForTest(PowerManagerInternal.class); LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock); @@ -96,8 +97,7 @@ public class VibrationScalerTest { @Test public void testGetExternalVibrationScale() { - when(mVibratorMock.getDefaultHapticFeedbackIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_LOW); + mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_LOW); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH); assertEquals(IExternalVibratorService.SCALE_VERY_HIGH, @@ -113,13 +113,11 @@ public class VibrationScalerTest { assertEquals(IExternalVibratorService.SCALE_NONE, mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH)); - when(mVibratorMock.getDefaultHapticFeedbackIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM); + mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM); assertEquals(IExternalVibratorService.SCALE_LOW, mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH)); - when(mVibratorMock.getDefaultHapticFeedbackIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_HIGH); + mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_HIGH); assertEquals(IExternalVibratorService.SCALE_VERY_LOW, mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH)); @@ -131,6 +129,45 @@ public class VibrationScalerTest { } @Test + public void scale_withCombined_resolvesAndScalesRecursively() { + setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_HIGH); + VibrationEffect prebaked = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK); + VibrationEffect oneShot = VibrationEffect.createOneShot(10, 10); + + CombinedVibrationEffect.Mono monoScaled = mVibrationScaler.scale( + CombinedVibrationEffect.createSynced(prebaked), + VibrationAttributes.USAGE_NOTIFICATION); + VibrationEffect.Prebaked prebakedScaled = (VibrationEffect.Prebaked) monoScaled.getEffect(); + assertEquals(prebakedScaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG); + + CombinedVibrationEffect.Stereo stereoScaled = mVibrationScaler.scale( + CombinedVibrationEffect.startSynced() + .addVibrator(1, prebaked) + .addVibrator(2, oneShot) + .combine(), + VibrationAttributes.USAGE_NOTIFICATION); + prebakedScaled = (VibrationEffect.Prebaked) stereoScaled.getEffects().get(1); + assertEquals(prebakedScaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG); + VibrationEffect.OneShot oneshotScaled = + (VibrationEffect.OneShot) stereoScaled.getEffects().get(2); + assertTrue(oneshotScaled.getAmplitude() > 0); + + CombinedVibrationEffect.Sequential sequentialScaled = mVibrationScaler.scale( + CombinedVibrationEffect.startSequential() + .addNext(CombinedVibrationEffect.createSynced(prebaked)) + .addNext(CombinedVibrationEffect.createSynced(oneShot)) + .combine(), + VibrationAttributes.USAGE_NOTIFICATION); + monoScaled = (CombinedVibrationEffect.Mono) sequentialScaled.getEffects().get(0); + prebakedScaled = (VibrationEffect.Prebaked) monoScaled.getEffect(); + assertEquals(prebakedScaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG); + monoScaled = (CombinedVibrationEffect.Mono) sequentialScaled.getEffects().get(1); + oneshotScaled = (VibrationEffect.OneShot) monoScaled.getEffect(); + assertTrue(oneshotScaled.getAmplitude() > 0); + } + + @Test public void scale_withPrebaked_setsEffectStrengthBasedOnSettings() { setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH); @@ -158,10 +195,31 @@ public class VibrationScalerTest { } @Test + public void scale_withPrebakedAndFallback_resolvesAndScalesRecursively() { + setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_HIGH); + VibrationEffect.OneShot fallback2 = (VibrationEffect.OneShot) VibrationEffect.createOneShot( + 10, VibrationEffect.DEFAULT_AMPLITUDE); + VibrationEffect.Prebaked fallback1 = new VibrationEffect.Prebaked( + VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_STRENGTH_MEDIUM, fallback2); + VibrationEffect.Prebaked effect = new VibrationEffect.Prebaked(VibrationEffect.EFFECT_CLICK, + VibrationEffect.EFFECT_STRENGTH_MEDIUM, fallback1); + + VibrationEffect.Prebaked scaled = mVibrationScaler.scale( + effect, VibrationAttributes.USAGE_NOTIFICATION); + VibrationEffect.Prebaked scaledFallback1 = + (VibrationEffect.Prebaked) scaled.getFallbackEffect(); + VibrationEffect.OneShot scaledFallback2 = + (VibrationEffect.OneShot) scaledFallback1.getFallbackEffect(); + assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG); + assertEquals(scaledFallback1.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG); + assertTrue(scaledFallback2.getAmplitude() > 0); + } + + @Test public void scale_withOneShotAndWaveform_resolvesAmplitude() { // No scale, default amplitude still resolved - when(mVibratorMock.getDefaultRingVibrationIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_LOW); + mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW); setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW); @@ -179,16 +237,13 @@ public class VibrationScalerTest { @Test public void scale_withOneShotWaveform_scalesAmplitude() { - when(mVibratorMock.getDefaultRingVibrationIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_LOW); + mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW); setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH); - when(mVibratorMock.getDefaultNotificationVibrationIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_HIGH); + mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_HIGH); setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW); - when(mVibratorMock.getDefaultHapticFeedbackIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM); + mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_MEDIUM); @@ -211,16 +266,13 @@ public class VibrationScalerTest { @Test public void scale_withComposed_scalesPrimitives() { - when(mVibratorMock.getDefaultRingVibrationIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_LOW); + mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW); setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH); - when(mVibratorMock.getDefaultNotificationVibrationIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_HIGH); + mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_HIGH); setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW); - when(mVibratorMock.getDefaultHapticFeedbackIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM); + mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_MEDIUM); 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 1e112da8fe7c..d8679876965c 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java @@ -76,26 +76,25 @@ public class VibrationSettingsTest { @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); - // TODO(b/131311651): replace with a FakeVibrator instead. - @Mock private Vibrator mVibratorMock; @Mock private VibrationSettings.OnVibratorSettingsChanged mListenerMock; @Mock private PowerManagerInternal mPowerManagerInternalMock; private TestLooper mTestLooper; private ContextWrapper mContextSpy; private AudioManager mAudioManager; + private FakeVibrator mFakeVibrator; private VibrationSettings mVibrationSettings; private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener; @Before public void setUp() throws Exception { mTestLooper = new TestLooper(); + mFakeVibrator = new FakeVibrator(); mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); - when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibratorMock); - when(mVibratorMock.hasVibrator()).thenReturn(true); + when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mFakeVibrator); doAnswer(invocation -> { mRegisteredPowerModeListener = invocation.getArgument(0); return null; @@ -305,12 +304,9 @@ public class VibrationSettingsTest { @Test public void getDefaultIntensity_returnsIntensityFromVibratorService() { - when(mVibratorMock.getDefaultHapticFeedbackIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_HIGH); - when(mVibratorMock.getDefaultNotificationVibrationIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM); - when(mVibratorMock.getDefaultRingVibrationIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_LOW); + mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_HIGH); + mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM); + mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW); setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF); @@ -336,12 +332,9 @@ public class VibrationSettingsTest { @Test public void getCurrentIntensity_returnsIntensityFromSettings() { - when(mVibratorMock.getDefaultHapticFeedbackIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_OFF); - when(mVibratorMock.getDefaultNotificationVibrationIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_OFF); - when(mVibratorMock.getDefaultRingVibrationIntensity()) - .thenReturn(Vibrator.VIBRATION_INTENSITY_OFF); + mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_OFF); + mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_OFF); + mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_OFF); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH); diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java new file mode 100644 index 000000000000..bee739231d3f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java @@ -0,0 +1,658 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.hardware.vibrator.IVibrator; +import android.os.CombinedVibrationEffect; +import android.os.IBinder; +import android.os.PowerManager; +import android.os.Process; +import android.os.SystemClock; +import android.os.VibrationAttributes; +import android.os.VibrationEffect; +import android.os.test.TestLooper; +import android.platform.test.annotations.Presubmit; +import android.util.SparseArray; + +import androidx.test.InstrumentationRegistry; + +import com.android.internal.app.IBatteryStats; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Tests for {@link VibrationThread}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:VibrationThreadTest + */ +@Presubmit +public class VibrationThreadTest { + + private static final int TEST_TIMEOUT_MILLIS = 1_000; + private static final int UID = Process.ROOT_UID; + private static final int VIBRATOR_ID = 1; + private static final String PACKAGE_NAME = "package"; + private static final VibrationAttributes ATTRS = new VibrationAttributes.Builder().build(); + + @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); + + @Mock private VibrationThread.VibrationCallbacks mThreadCallbacks; + @Mock private VibratorController.OnVibrationCompleteListener mControllerCallbacks; + @Mock private IBinder mVibrationToken; + @Mock private IBatteryStats mIBatteryStatsMock; + + private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>(); + private PowerManager.WakeLock mWakeLock; + private TestLooper mTestLooper; + + @Before + public void setUp() throws Exception { + mTestLooper = new TestLooper(); + mWakeLock = InstrumentationRegistry.getContext().getSystemService( + PowerManager.class).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*"); + + mockVibrators(VIBRATOR_ID); + } + + @Test + public void vibrate_noVibrator_ignoresVibration() { + mVibratorProviders.clear(); + long vibrationId = 1; + CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced( + VibrationEffect.get(VibrationEffect.EFFECT_CLICK)); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId)); + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.IGNORED)); + } + + @Test + public void vibrate_missingVibrators_ignoresVibration() { + long vibrationId = 1; + CombinedVibrationEffect effect = CombinedVibrationEffect.startSequential() + .addNext(2, VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) + .addNext(3, VibrationEffect.get(VibrationEffect.EFFECT_TICK)) + .combine(); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId)); + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.IGNORED)); + } + + @Test + public void vibrate_singleVibratorOneShot_runsVibrationAndSetsAmplitude() throws Exception { + mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + + long vibrationId = 1; + VibrationEffect effect = VibrationEffect.createOneShot(10, 100); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L)); + verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); + verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + + assertEquals(Arrays.asList(expectedOneShot(10)), + mVibratorProviders.get(VIBRATOR_ID).getEffects()); + assertEquals(Arrays.asList(100), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()); + } + + @Test + public void vibrate_oneShotWithoutAmplitudeControl_runsVibrationWithDefaultAmplitude() + throws Exception { + long vibrationId = 1; + VibrationEffect effect = VibrationEffect.createOneShot(10, 100); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L)); + verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); + verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + + assertEquals(Arrays.asList(expectedOneShot(10)), + mVibratorProviders.get(VIBRATOR_ID).getEffects()); + assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty()); + } + + @Test + public void vibrate_singleVibratorWaveform_runsVibrationAndChangesAmplitudes() + throws Exception { + mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + + long vibrationId = 1; + VibrationEffect effect = VibrationEffect.createWaveform( + new long[]{5, 5, 5}, new int[]{1, 2, 3}, -1); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(15L)); + verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); + verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + + assertEquals(Arrays.asList(expectedOneShot(15)), + mVibratorProviders.get(VIBRATOR_ID).getEffects()); + assertEquals(Arrays.asList(1, 2, 3), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()); + } + + @Test + public void vibrate_singleVibratorRepeatingWaveform_runsVibrationUntilThreadCancelled() + throws Exception { + mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + + long vibrationId = 1; + int[] amplitudes = new int[]{1, 2, 3}; + VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5, 5, 5}, amplitudes, 0); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + + Thread.sleep(35); + // Vibration still running after 2 cycles. + assertTrue(thread.isAlive()); + assertTrue(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + + thread.cancel(); + waitForCompletion(thread); + + verify(mIBatteryStatsMock, never()).noteVibratorOn(eq(UID), anyLong()); + verify(mIBatteryStatsMock, never()).noteVibratorOff(eq(UID)); + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED)); + assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + + List<Integer> playedAmplitudes = mVibratorProviders.get(VIBRATOR_ID).getAmplitudes(); + assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffects().isEmpty()); + assertFalse(playedAmplitudes.isEmpty()); + + for (int i = 0; i < playedAmplitudes.size(); i++) { + assertEquals(amplitudes[i % amplitudes.length], playedAmplitudes.get(i).intValue()); + } + } + + @Test + public void vibrate_singleVibratorPrebaked_runsVibration() throws Exception { + mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_THUD); + + long vibrationId = 1; + VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_THUD); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L)); + verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); + verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + + assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_THUD)), + mVibratorProviders.get(VIBRATOR_ID).getEffects()); + } + + @Test + public void vibrate_singleVibratorPrebakedAndUnsupportedEffectWithFallback_runsFallback() + throws Exception { + mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + + long vibrationId = 1; + VibrationEffect fallback = VibrationEffect.createOneShot(10, 100); + VibrationEffect.Prebaked effect = new VibrationEffect.Prebaked(VibrationEffect.EFFECT_CLICK, + VibrationEffect.EFFECT_STRENGTH_STRONG, fallback); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L)); + verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); + verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + + assertEquals(Arrays.asList(expectedOneShot(10)), + mVibratorProviders.get(VIBRATOR_ID).getEffects()); + assertEquals(Arrays.asList(100), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()); + } + + @Test + public void vibrate_singleVibratorPrebakedAndUnsupportedEffect_ignoresVibration() + throws Exception { + long vibrationId = 1; + VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + verify(mIBatteryStatsMock, never()).noteVibratorOn(eq(UID), anyLong()); + verify(mIBatteryStatsMock, never()).noteVibratorOff(eq(UID)); + verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), + eq(Vibration.Status.IGNORED_UNSUPPORTED)); + assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffects().isEmpty()); + } + + @Test + public void vibrate_singleVibratorComposed_runsVibration() throws Exception { + mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + + long vibrationId = 1; + VibrationEffect effect = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f) + .compose(); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(40L)); + verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); + verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertEquals(Arrays.asList(effect), mVibratorProviders.get(VIBRATOR_ID).getEffects()); + } + + @Test + public void vibrate_singleVibratorComposedAndNoCapability_ignoresVibration() throws Exception { + long vibrationId = 1; + VibrationEffect effect = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f) + .compose(); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + verify(mIBatteryStatsMock, never()).noteVibratorOn(eq(UID), anyLong()); + verify(mIBatteryStatsMock, never()).noteVibratorOff(eq(UID)); + verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), + eq(Vibration.Status.IGNORED_UNSUPPORTED)); + assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffects().isEmpty()); + } + + @Test + public void vibrate_singleVibratorCancelled_vibratorStopped() throws Exception { + long vibrationId = 1; + VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5}, new int[]{100}, 0); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + + Thread.sleep(15); + // Vibration still running after 2 cycles. + assertTrue(thread.isAlive()); + assertTrue(thread.getVibrators().get(1).isVibrating()); + + thread.binderDied(); + waitForCompletion(thread); + assertFalse(thread.getVibrators().get(1).isVibrating()); + + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED)); + } + + @Test + public void vibrate_multipleExistingAndMissingVibrators_vibratesOnlyExistingOnes() + throws Exception { + mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_TICK); + + long vibrationId = 1; + CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced() + .addVibrator(VIBRATOR_ID, VibrationEffect.get(VibrationEffect.EFFECT_TICK)) + .addVibrator(2, VibrationEffect.get(VibrationEffect.EFFECT_TICK)) + .combine(); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L)); + verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); + verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); + verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibrationId)); + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + + assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_TICK)), + mVibratorProviders.get(VIBRATOR_ID).getEffects()); + } + + @Test + public void vibrate_multipleMono_runsSameEffectInAllVibrators() throws Exception { + mockVibrators(1, 2, 3); + mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + mVibratorProviders.get(2).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + mVibratorProviders.get(3).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + + long vibrationId = 1; + CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced( + VibrationEffect.get(VibrationEffect.EFFECT_CLICK)); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L)); + verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); + verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId)); + verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId)); + verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId)); + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + assertFalse(thread.getVibrators().get(1).isVibrating()); + assertFalse(thread.getVibrators().get(2).isVibrating()); + assertFalse(thread.getVibrators().get(3).isVibrating()); + + VibrationEffect expected = expectedPrebaked(VibrationEffect.EFFECT_CLICK); + assertEquals(Arrays.asList(expected), mVibratorProviders.get(1).getEffects()); + assertEquals(Arrays.asList(expected), mVibratorProviders.get(2).getEffects()); + assertEquals(Arrays.asList(expected), mVibratorProviders.get(3).getEffects()); + } + + @Test + public void vibrate_multipleStereo_runsVibrationOnRightVibrators() throws Exception { + mockVibrators(1, 2, 3, 4); + mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + mVibratorProviders.get(3).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + mVibratorProviders.get(4).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + + long vibrationId = 1; + VibrationEffect composed = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .compose(); + CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced() + .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) + .addVibrator(2, VibrationEffect.createOneShot(10, 100)) + .addVibrator(3, VibrationEffect.createWaveform( + new long[]{10, 10}, new int[]{1, 2}, -1)) + .addVibrator(4, composed) + .combine(); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L)); + verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); + verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId)); + verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId)); + verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId)); + verify(mControllerCallbacks).onComplete(eq(4), eq(vibrationId)); + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + assertFalse(thread.getVibrators().get(1).isVibrating()); + assertFalse(thread.getVibrators().get(2).isVibrating()); + assertFalse(thread.getVibrators().get(3).isVibrating()); + assertFalse(thread.getVibrators().get(4).isVibrating()); + + assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)), + mVibratorProviders.get(1).getEffects()); + assertEquals(Arrays.asList(expectedOneShot(10)), mVibratorProviders.get(2).getEffects()); + assertEquals(Arrays.asList(100), mVibratorProviders.get(2).getAmplitudes()); + assertEquals(Arrays.asList(expectedOneShot(20)), mVibratorProviders.get(3).getEffects()); + assertEquals(Arrays.asList(1, 2), mVibratorProviders.get(3).getAmplitudes()); + assertEquals(Arrays.asList(composed), mVibratorProviders.get(4).getEffects()); + } + + @Test + public void vibrate_multipleSequential_runsVibrationInOrderWithDelays() + throws Exception { + mockVibrators(1, 2, 3); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + mVibratorProviders.get(3).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + + long vibrationId = 1; + VibrationEffect composed = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .compose(); + CombinedVibrationEffect effect = CombinedVibrationEffect.startSequential() + .addNext(3, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), /* delay= */ 50) + .addNext(1, VibrationEffect.createOneShot(10, 100), /* delay= */ 50) + .addNext(2, composed, /* delay= */ 50) + .combine(); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + + waitForCompletion(thread); + InOrder controllerVerifier = inOrder(mControllerCallbacks); + controllerVerifier.verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId)); + controllerVerifier.verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId)); + controllerVerifier.verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId)); + + InOrder batterVerifier = inOrder(mIBatteryStatsMock); + batterVerifier.verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L)); + batterVerifier.verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); + batterVerifier.verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L)); + batterVerifier.verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); + batterVerifier.verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L)); + batterVerifier.verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); + + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + assertFalse(thread.getVibrators().get(1).isVibrating()); + assertFalse(thread.getVibrators().get(2).isVibrating()); + assertFalse(thread.getVibrators().get(3).isVibrating()); + + assertEquals(Arrays.asList(expectedOneShot(10)), mVibratorProviders.get(1).getEffects()); + assertEquals(Arrays.asList(100), mVibratorProviders.get(1).getAmplitudes()); + assertEquals(Arrays.asList(composed), mVibratorProviders.get(2).getEffects()); + assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)), + mVibratorProviders.get(3).getEffects()); + } + + @Test + public void vibrate_multipleWaveforms_playsWaveformsInParallel() throws Exception { + mockVibrators(1, 2, 3); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + mVibratorProviders.get(3).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + + long vibrationId = 1; + CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced() + .addVibrator(1, VibrationEffect.createWaveform( + new long[]{5, 10, 10}, new int[]{1, 2, 3}, -1)) + .addVibrator(2, VibrationEffect.createWaveform( + new long[]{20, 60}, new int[]{4, 5}, -1)) + .addVibrator(3, VibrationEffect.createWaveform( + new long[]{60}, new int[]{6}, -1)) + .combine(); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + + Thread.sleep(40); + // First waveform has finished. + verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId)); + assertEquals(Arrays.asList(1, 2, 3), mVibratorProviders.get(1).getAmplitudes()); + // Second waveform is halfway through. + assertEquals(Arrays.asList(4, 5), mVibratorProviders.get(2).getAmplitudes()); + // Third waveform is almost ending. + assertEquals(Arrays.asList(6), mVibratorProviders.get(3).getAmplitudes()); + + waitForCompletion(thread); + + verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(80L)); + verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); + verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId)); + verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId)); + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + assertFalse(thread.getVibrators().get(1).isVibrating()); + assertFalse(thread.getVibrators().get(2).isVibrating()); + assertFalse(thread.getVibrators().get(3).isVibrating()); + + assertEquals(Arrays.asList(expectedOneShot(25)), mVibratorProviders.get(1).getEffects()); + assertEquals(Arrays.asList(expectedOneShot(80)), mVibratorProviders.get(2).getEffects()); + assertEquals(Arrays.asList(expectedOneShot(60)), mVibratorProviders.get(3).getEffects()); + assertEquals(Arrays.asList(1, 2, 3), mVibratorProviders.get(1).getAmplitudes()); + assertEquals(Arrays.asList(4, 5), mVibratorProviders.get(2).getAmplitudes()); + assertEquals(Arrays.asList(6), mVibratorProviders.get(3).getAmplitudes()); + } + + @Test + public void vibrate_withWaveform_totalVibrationTimeRespected() { + int totalDuration = 10_000; // 10s + int stepDuration = 25; // 25ms + + // 25% of the first waveform step will be spent on the native on() call. + // 25% of each waveform step will be spent on the native setAmplitude() call.. + mVibratorProviders.get(VIBRATOR_ID).setLatency(stepDuration / 4); + mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + + int stepCount = totalDuration / stepDuration; + long[] timings = new long[stepCount]; + int[] amplitudes = new int[stepCount]; + Arrays.fill(timings, stepDuration); + Arrays.fill(amplitudes, VibrationEffect.DEFAULT_AMPLITUDE); + VibrationEffect effect = VibrationEffect.createWaveform(timings, amplitudes, -1); + + long vibrationId = 1; + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + long startTime = SystemClock.elapsedRealtime(); + + waitForCompletion(thread, totalDuration + TEST_TIMEOUT_MILLIS); + long delay = Math.abs(SystemClock.elapsedRealtime() - startTime - totalDuration); + + // Allow some delay for thread scheduling and callback triggering. + int maxDelay = (int) (0.05 * totalDuration); // < 5% of total duration + assertTrue("Waveform with perceived delay of " + delay + "ms," + + " expected less than " + maxDelay + "ms", + delay < maxDelay); + } + + @Test + public void vibrate_multipleCancelled_allVibratorsStopped() throws Exception { + mockVibrators(1, 2, 3); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + mVibratorProviders.get(3).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + + long vibrationId = 1; + CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced() + .addVibrator(1, VibrationEffect.createWaveform( + new long[]{5, 10}, new int[]{1, 2}, 0)) + .addVibrator(2, VibrationEffect.createWaveform( + new long[]{20, 30}, new int[]{3, 4}, 0)) + .addVibrator(3, VibrationEffect.createWaveform( + new long[]{10, 40}, new int[]{5, 6}, 0)) + .combine(); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + + Thread.sleep(15); + assertTrue(thread.isAlive()); + assertTrue(thread.getVibrators().get(1).isVibrating()); + assertTrue(thread.getVibrators().get(2).isVibrating()); + assertTrue(thread.getVibrators().get(3).isVibrating()); + + thread.cancel(); + waitForCompletion(thread); + assertFalse(thread.getVibrators().get(1).isVibrating()); + assertFalse(thread.getVibrators().get(2).isVibrating()); + assertFalse(thread.getVibrators().get(3).isVibrating()); + + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED)); + } + + @Test + public void vibrate_binderDied_cancelsVibration() throws Exception { + long vibrationId = 1; + VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5}, new int[]{100}, 0); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + + Thread.sleep(15); + // Vibration still running after 2 cycles. + assertTrue(thread.isAlive()); + assertTrue(thread.getVibrators().get(1).isVibrating()); + + thread.binderDied(); + waitForCompletion(thread); + + verify(mVibrationToken).linkToDeath(same(thread), eq(0)); + verify(mVibrationToken).unlinkToDeath(same(thread), eq(0)); + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED)); + assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffects().isEmpty()); + assertFalse(thread.getVibrators().get(1).isVibrating()); + } + + private void mockVibrators(int... vibratorIds) { + for (int vibratorId : vibratorIds) { + mVibratorProviders.put(vibratorId, + new FakeVibratorControllerProvider(mTestLooper.getLooper())); + } + } + + private VibrationThread startThreadAndDispatcher(long vibrationId, VibrationEffect effect) { + return startThreadAndDispatcher(vibrationId, CombinedVibrationEffect.createSynced(effect)); + } + + private VibrationThread startThreadAndDispatcher(long vibrationId, + CombinedVibrationEffect effect) { + VibrationThread thread = new VibrationThread(createVibration(vibrationId, effect), + createVibratorControllers(), mWakeLock, mIBatteryStatsMock, mThreadCallbacks); + doAnswer(answer -> { + thread.vibratorComplete(answer.getArgument(0)); + return null; + }).when(mControllerCallbacks).onComplete(anyInt(), eq(vibrationId)); + mTestLooper.startAutoDispatch(); + thread.start(); + return thread; + } + + private void waitForCompletion(VibrationThread thread) { + waitForCompletion(thread, TEST_TIMEOUT_MILLIS); + } + + private void waitForCompletion(VibrationThread thread, long timeout) { + try { + thread.join(timeout); + } catch (InterruptedException e) { + } + assertFalse(thread.isAlive()); + mTestLooper.dispatchAll(); + } + + private Vibration createVibration(long id, CombinedVibrationEffect effect) { + return new Vibration(mVibrationToken, (int) id, effect, ATTRS, UID, PACKAGE_NAME, "reason"); + } + + private SparseArray<VibratorController> createVibratorControllers() { + SparseArray<VibratorController> array = new SparseArray<>(); + for (Map.Entry<Integer, FakeVibratorControllerProvider> e : mVibratorProviders.entrySet()) { + int id = e.getKey(); + array.put(id, e.getValue().newVibratorController(id, mControllerCallbacks)); + } + return array; + } + + private VibrationEffect expectedOneShot(long millis) { + return VibrationEffect.createOneShot(millis, VibrationEffect.DEFAULT_AMPLITUDE); + } + + private VibrationEffect expectedPrebaked(int effectId) { + return new VibrationEffect.Prebaked(effectId, false, + VibrationEffect.EFFECT_STRENGTH_MEDIUM); + } +} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 4d2a4784b5d9..8c744c94249b 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -95,6 +95,7 @@ import android.provider.Settings.Secure; import android.service.notification.ConversationChannelWrapper; import android.test.suitebuilder.annotation.SmallTest; import android.testing.TestableContentResolver; +import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IntArray; @@ -3293,6 +3294,95 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test + public void testDeleted_noTime() throws Exception { + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager, mStatsEventBuilderFactory); + + final String xml = "<ranking version=\"1\">\n" + + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" + + "<channel id=\"id\" name=\"hi\" importance=\"3\" deleted=\"true\"/>" + + "</package>" + + "</ranking>"; + TypedXmlPullParser parser = Xml.newFastPullParser(); + parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), + null); + parser.nextTag(); + mHelper.readXml(parser, false, UserHandle.USER_ALL); + + assertNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true)); + } + + @Test + public void testDeleted_recentTime() throws Exception { + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager, mStatsEventBuilderFactory); + + mHelper.createNotificationChannel( + PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false); + mHelper.deleteNotificationChannel(PKG_P, UID_P, "id"); + NotificationChannel nc1 = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true); + assertTrue(DateUtils.isToday(nc1.getDeletedTimeMs())); + assertTrue(nc1.isDeleted()); + + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_P, UID_P, false, + UserHandle.USER_SYSTEM, "id", NotificationChannel.DEFAULT_CHANNEL_ID); + + TypedXmlPullParser parser = Xml.newFastPullParser(); + parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), + null); + parser.nextTag(); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager, mStatsEventBuilderFactory); + mHelper.readXml(parser, true, UserHandle.USER_SYSTEM); + + NotificationChannel nc = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true); + assertTrue(DateUtils.isToday(nc.getDeletedTimeMs())); + assertTrue(nc.isDeleted()); + } + + @Test + public void testUnDelete_time() throws Exception { + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager, mStatsEventBuilderFactory); + + mHelper.createNotificationChannel( + PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false); + mHelper.deleteNotificationChannel(PKG_P, UID_P, "id"); + NotificationChannel nc1 = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true); + assertTrue(DateUtils.isToday(nc1.getDeletedTimeMs())); + assertTrue(nc1.isDeleted()); + + mHelper.createNotificationChannel( + PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false); + nc1 = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true); + assertEquals(-1, nc1.getDeletedTimeMs()); + assertFalse(nc1.isDeleted()); + } + + @Test + public void testDeleted_longTime() throws Exception { + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager, mStatsEventBuilderFactory); + + long time = System.currentTimeMillis() - (DateUtils.DAY_IN_MILLIS * 30); + + final String xml = "<ranking version=\"1\">\n" + + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" + + "<channel id=\"id\" name=\"hi\" importance=\"3\" deleted=\"true\" del_time=\"" + + time + "\"/>" + + "</package>" + + "</ranking>"; + TypedXmlPullParser parser = Xml.newFastPullParser(); + parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), + null); + parser.nextTag(); + mHelper.readXml(parser, false, UserHandle.USER_ALL); + + NotificationChannel nc = mHelper.getNotificationChannel(PKG_O, UID_O, "id", true); + assertNull(nc); + } + + @Test public void testGetConversations_all() { String convoId = "convo"; NotificationChannel messages = 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 610edc0a5e14..6f584ee9a18c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -79,6 +79,7 @@ import android.app.ActivityOptions; import android.app.WindowConfiguration; import android.app.servertransaction.ActivityConfigurationChangeItem; import android.app.servertransaction.ClientTransaction; +import android.app.servertransaction.DestroyActivityItem; import android.app.servertransaction.PauseActivityItem; import android.content.ComponentName; import android.content.Intent; @@ -1449,6 +1450,19 @@ public class ActivityRecordTests extends WindowTestsBase { } @Test + public void testRemoveImmediately() throws RemoteException { + final ActivityRecord activity = createActivityWithTask(); + final WindowProcessController wpc = activity.app; + activity.getTask().removeImmediately("test"); + + verify(mAtm.getLifecycleManager()).scheduleTransaction(any(), eq(activity.appToken), + isA(DestroyActivityItem.class)); + assertNull(activity.app); + assertEquals(DESTROYED, activity.getState()); + assertFalse(wpc.hasActivities()); + } + + @Test public void testRemoveFromHistory() { final ActivityRecord activity = createActivityWithTask(); final Task rootTask = activity.getRootTask(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 9527625d50ff..11be74d1a8c7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -540,6 +540,25 @@ public class DisplayContentTests extends WindowTestsBase { assertFalse(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot()); } + @Test + public void testImeIsAttachedToDisplayForLetterboxedApp() { + final DisplayContent dc = mDisplayContent; + final WindowState ws = createWindow(null, TYPE_APPLICATION, dc, "app window"); + dc.setImeLayeringTarget(ws); + + // Adjust bounds so that matchesRootDisplayAreaBounds() returns false and + // hence isLetterboxedAppWindow() returns true. + ws.mActivityRecord.getConfiguration().windowConfiguration.setBounds(new Rect(1, 1, 1, 1)); + assertFalse("matchesRootDisplayAreaBounds() should return false", + ws.matchesRootDisplayAreaBounds()); + assertTrue("isLetterboxedAppWindow() should return true", ws.isLetterboxedAppWindow()); + assertTrue("IME shouldn't be attached to app", + dc.computeImeParent() != dc.getImeTarget(IME_TARGET_LAYERING).getWindow() + .mActivityRecord.getSurfaceControl()); + assertEquals("IME should be attached to display", + dc.getImeContainer().getParent().getSurfaceControl(), dc.computeImeParent()); + } + private WindowState[] createNotDrawnWindowsOn(DisplayContent displayContent, int... types) { final WindowState[] windows = new WindowState[types.length]; for (int i = 0; i < types.length; i++) { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 0f03f68eccb6..06784affefe0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -123,6 +123,15 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { updateDisplayFrames(); } + void addWindowWithRawInsetsState(WindowState win) { + addWindow(win); + // Without mPerformLayout in display content, the window cannot see any insets. Override the + // insets state with the global one. + final InsetsState insetsState = + win.getDisplayContent().getInsetsStateController().getRawInsetsState(); + win.mAboveInsetsState = insetsState; + } + public void setRotation(int rotation, boolean includingWindows) { mRotation = rotation; updateDisplayFrames(); @@ -269,7 +278,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { @Test public void layoutWindowLw_fitStatusBars() { mWindow.mAttrs.setFitInsetsTypes(Type.statusBars()); - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -281,7 +290,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { @Test public void layoutWindowLw_fitNavigationBars() { mWindow.mAttrs.setFitInsetsTypes(Type.navigationBars()); - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -293,7 +302,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { @Test public void layoutWindowLw_fitAllSides() { mWindow.mAttrs.setFitInsetsSides(Side.all()); - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -305,7 +314,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { @Test public void layoutWindowLw_fitTopOnly() { mWindow.mAttrs.setFitInsetsSides(Side.TOP); - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -316,11 +325,12 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { @Test public void layoutWindowLw_fitInsetsIgnoringVisibility() { - final InsetsState state = mWindow.getInsetsState(); + final InsetsState state = + mDisplayContent.getInsetsStateController().getRawInsetsState(); state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false); mWindow.mAttrs.setFitInsetsIgnoringVisibility(true); - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -331,11 +341,12 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { @Test public void layoutWindowLw_fitInsetsNotIgnoringVisibility() { - final InsetsState state = mWindow.getInsetsState(); + final InsetsState state = + mDisplayContent.getInsetsStateController().getRawInsetsState(); state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false); mWindow.mAttrs.setFitInsetsIgnoringVisibility(false); - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -352,8 +363,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { state.getSource(InsetsState.ITYPE_IME).setFrame( 0, DISPLAY_HEIGHT - IME_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT); mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; - mWindow.mBehindIme = true; - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -368,7 +378,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.setFitInsetsTypes(Type.displayCutout()); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -384,7 +394,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -401,7 +411,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -418,7 +428,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -435,7 +445,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -453,7 +463,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mWindow.mAttrs.setFitInsetsTypes( mWindow.mAttrs.getFitInsetsTypes() & ~Type.statusBars()); - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -469,11 +479,12 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - mWindow.getInsetsState().getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); + mDisplayContent.getInsetsStateController().getRawInsetsState() + .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); final InsetsState requestedState = new InsetsState(); requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false); mWindow.updateRequestedVisibility(requestedState); - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -489,12 +500,13 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - mWindow.getInsetsState().getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); + mDisplayContent.getInsetsStateController().getRawInsetsState() + .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); final InsetsState requestedState = new InsetsState(); requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false); mWindow.updateRequestedVisibility(requestedState); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -511,7 +523,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -528,7 +540,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -547,7 +559,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mWindow.mAttrs.setFitInsetsTypes( mWindow.mAttrs.getFitInsetsTypes() & ~Type.statusBars()); - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -564,7 +576,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY; mWindow.mAttrs.width = DISPLAY_WIDTH; mWindow.mAttrs.height = DISPLAY_HEIGHT; - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -584,7 +596,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.setFitInsetsTypes( mWindow.mAttrs.getFitInsetsTypes() & ~Type.statusBars()); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -599,7 +611,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -616,7 +628,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -633,7 +645,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -650,7 +662,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -665,7 +677,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_NOTHING; - addWindow(mWindow); + addWindowWithRawInsetsState(mWindow); final int forwardedInsetBottom = 50; mDisplayPolicy.setForwardedInsets(Insets.of(0, 0, 0, forwardedInsetBottom)); @@ -815,9 +827,13 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.beginLayoutLw(mFrames, mDisplayContent.getConfiguration().uiMode); doReturn((mDisplayContent.getRotation() + 1) % 4).when(mDisplayContent) .rotationForActivityInDifferentOrientation(eq(mWindow.mActivityRecord)); - final Rect frame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame(); + mWindow.mAboveInsetsState.addSource(mDisplayContent.getInsetsStateController() + .getRawInsetsState().peekSource(ITYPE_STATUS_BAR)); + final Rect frame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow) + .getSource(ITYPE_STATUS_BAR).getFrame(); mDisplayContent.rotateInDifferentOrientationIfNeeded(mWindow.mActivityRecord); - final Rect rotatedFrame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame(); + final Rect rotatedFrame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow) + .getSource(ITYPE_STATUS_BAR).getFrame(); assertEquals(DISPLAY_WIDTH, frame.width()); assertEquals(DISPLAY_HEIGHT, rotatedFrame.width()); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index 79b2da187680..22ee7e4f229f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -22,8 +22,6 @@ import static android.view.Surface.ROTATION_0; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; @@ -32,13 +30,11 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT; @@ -47,7 +43,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -293,46 +288,6 @@ public class DisplayPolicyTests extends WindowTestsBase { return win; } - @UseTestDisplay( - addWindows = { W_ACTIVITY, W_STATUS_BAR, W_NAVIGATION_BAR, W_NOTIFICATION_SHADE }) - @Test - public void testUpdateHideNavInputEventReceiver() { - final InsetsPolicy insetsPolicy = mDisplayContent.getInsetsPolicy(); - final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); - displayPolicy.addWindowLw(mStatusBarWindow, mStatusBarWindow.mAttrs); - displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs); - displayPolicy.addWindowLw(mNotificationShadeWindow, mNotificationShadeWindow.mAttrs); - spyOn(displayPolicy); - doReturn(true).when(displayPolicy).hasNavigationBar(); - - // App doesn't request to hide navigation bar. - insetsPolicy.updateBarControlTarget(mAppWindow); - assertNull(displayPolicy.mInputConsumer); - - // App requests to hide navigation bar. - final InsetsState requestedState = new InsetsState(); - requestedState.getSource(ITYPE_NAVIGATION_BAR).setVisible(false); - mAppWindow.updateRequestedVisibility(requestedState); - insetsPolicy.onInsetsModified(mAppWindow); - assertNotNull(displayPolicy.mInputConsumer); - - // App still requests to hide navigation bar, but without BEHAVIOR_SHOW_BARS_BY_TOUCH. - mAppWindow.mAttrs.insetsFlags.behavior = BEHAVIOR_SHOW_BARS_BY_SWIPE; - insetsPolicy.updateBarControlTarget(mAppWindow); - assertNull(displayPolicy.mInputConsumer); - - // App still requests to hide navigation bar, but with BEHAVIOR_SHOW_BARS_BY_TOUCH. - mAppWindow.mAttrs.insetsFlags.behavior = BEHAVIOR_SHOW_BARS_BY_TOUCH; - insetsPolicy.updateBarControlTarget(mAppWindow); - assertNotNull(displayPolicy.mInputConsumer); - - // App still requests to hide navigation bar with BEHAVIOR_SHOW_BARS_BY_TOUCH, - // but notification shade forcibly shows navigation bar - mNotificationShadeWindow.mAttrs.privateFlags |= PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; - insetsPolicy.updateBarControlTarget(mAppWindow); - assertNull(displayPolicy.mInputConsumer); - } - @UseTestDisplay(addWindows = { W_NAVIGATION_BAR, W_INPUT_METHOD }) @Test public void testImeMinimalSourceFrame() { @@ -346,6 +301,8 @@ public class DisplayPolicyTests extends WindowTestsBase { displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs); mNavBarWindow.getControllableInsetProvider().setServerVisible(true); + final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState(); + mImeWindow.mAboveInsetsState = state; mDisplayContent.setInputMethodWindowLocked(mImeWindow); mImeWindow.mAttrs.setFitInsetsSides(Side.all() & ~Side.BOTTOM); @@ -355,7 +312,6 @@ public class DisplayPolicyTests extends WindowTestsBase { displayPolicy.beginLayoutLw(mDisplayContent.mDisplayFrames, 0 /* UI mode */); displayPolicy.layoutWindowLw(mImeWindow, null, mDisplayContent.mDisplayFrames); - final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState(); final InsetsSource imeSource = state.peekSource(ITYPE_IME); final InsetsSource navBarSource = state.peekSource(ITYPE_NAVIGATION_BAR); diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java index 61911b3c36be..e6f24da3e7b9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java @@ -33,6 +33,7 @@ import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.wm.SizeCompatTests.prepareLimitedBounds; import static com.android.server.wm.SizeCompatTests.prepareUnresizable; import static com.android.server.wm.SizeCompatTests.rotateDisplay; @@ -309,6 +310,43 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { assertThat(mSecondRoot.findAreaForToken(imeToken)).isEqualTo(imeContainer); } + @Test + public void testResizableFixedOrientationApp_taskLevelLetterboxing() { + mFirstRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); + + // Launch portrait on first DAG + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + prepareLimitedBounds(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT, + false /* isUnresizable */); + + // Display in landscape (as opposite to DAG), first DAG and activity in portrait + assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); + assertThat(mFirstRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT); + assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT); + assertThat(mFirstTask.isTaskLetterboxed()).isFalse(); + assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); + + // Launch portrait on second DAG + mDisplay.onLastFocusedTaskDisplayAreaChanged(mSecondTda); + prepareLimitedBounds(mSecondActivity, SCREEN_ORIENTATION_LANDSCAPE, + false /* isUnresizable */); + + // Display in portrait (as opposite to DAG), first DAG and activity in landscape + assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT); + assertThat(mSecondRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE); + assertThat(mSecondActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE); + assertThat(mSecondTask.isTaskLetterboxed()).isFalse(); + assertThat(mSecondActivity.inSizeCompatMode()).isFalse(); + + // First activity is letterboxed in portrait as requested. + assertThat(mFirstRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE); + assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT); + assertThat(mFirstTask.isTaskLetterboxed()).isTrue(); + assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); + + } + private void setupImeWindow() { final WindowState imeWindow = createWindow(null /* parent */, TYPE_INPUT_METHOD, mDisplay, "mImeWindow"); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java index e0fd3796f2aa..bf3ed692dc8e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -45,6 +45,7 @@ import static org.mockito.Mockito.verify; import android.app.StatusBarManager; import android.platform.test.annotations.Presubmit; +import android.view.InsetsSource; import android.view.InsetsSourceControl; import android.view.InsetsState; @@ -272,7 +273,6 @@ public class InsetsPolicyTest extends WindowTestsBase { final WindowState navBar = addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar"); navBar.setHasSurface(true); navBar.getControllableInsetProvider().setServerVisible(true); - final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy()); doNothing().when(policy).startAnimation(anyBoolean(), any()); @@ -337,11 +337,14 @@ public class InsetsPolicyTest extends WindowTestsBase { @UseTestDisplay(addWindows = W_ACTIVITY) @Test public void testAbortTransientBars_bothCanBeAborted_appGetsBothRealControls() { - addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar") - .getControllableInsetProvider().getSource().setVisible(false); - addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar") - .getControllableInsetProvider().getSource().setVisible(false); - + final InsetsSource statusBarSource = addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar") + .getControllableInsetProvider().getSource(); + final InsetsSource navBarSource = addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar") + .getControllableInsetProvider().getSource(); + statusBarSource.setVisible(false); + navBarSource.setVisible(false); + mAppWindow.mAboveInsetsState.addSource(navBarSource); + mAppWindow.mAboveInsetsState.addSource(statusBarSource); final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy()); doNothing().when(policy).startAnimation(anyBoolean(), any()); policy.updateBarControlTarget(mAppWindow); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index 276643847712..2107ab1eeeea 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -59,25 +59,6 @@ import org.junit.runner.RunWith; public class InsetsStateControllerTest extends WindowTestsBase { @Test - public void testStripForDispatch_notOwn() { - final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); - getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null); - statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR)); - assertNotNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR)); - } - - @Test - public void testStripForDispatch_own() { - final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); - mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR) - .setWindow(statusBar, null, null); - statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR)); - final InsetsState state = getController().getInsetsForWindow(statusBar); - assertNull(state.peekSource(ITYPE_STATUS_BAR)); - } - - @Test public void testStripForDispatch_navBar() { final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar"); final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); @@ -142,14 +123,15 @@ public class InsetsStateControllerTest extends WindowTestsBase { getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null); final WindowState app1 = createWindow(null, TYPE_APPLICATION, "app1"); - app1.mBehindIme = true; - final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2"); - app2.mBehindIme = false; + + app1.mAboveInsetsState.addSource(getController().getRawInsetsState().getSource(ITYPE_IME)); getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true); - assertFalse(getController().getInsetsForWindow(app2).getSource(ITYPE_IME).isVisible()); - assertTrue(getController().getInsetsForWindow(app1).getSource(ITYPE_IME).isVisible()); + assertFalse(getController().getInsetsForWindow(app2).getSource(ITYPE_IME) + .isVisible()); + assertTrue(getController().getInsetsForWindow(app1).getSource(ITYPE_IME) + .isVisible()); } @UseTestDisplay(addWindows = W_INPUT_METHOD) @@ -158,7 +140,8 @@ public class InsetsStateControllerTest extends WindowTestsBase { getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null); final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); - app.mBehindIme = true; + app.mAboveInsetsState.getSource(ITYPE_IME).setVisible(true); + app.mAboveInsetsState.getSource(ITYPE_IME).setFrame(mImeWindow.getFrame()); getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true); assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible()); @@ -170,10 +153,10 @@ public class InsetsStateControllerTest extends WindowTestsBase { getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null); final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); - app.mBehindIme = false; getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true); - assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible()); + assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME) + .isVisible()); } @UseTestDisplay(addWindows = W_INPUT_METHOD) @@ -210,7 +193,8 @@ public class InsetsStateControllerTest extends WindowTestsBase { // app won't get visible IME insets while above IME even when IME is visible. assertTrue(getController().getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME)); - assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible()); + assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME) + .isVisible()); // Reset invocation counter. clearInvocations(app); @@ -219,6 +203,8 @@ public class InsetsStateControllerTest extends WindowTestsBase { app.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE; mDisplayContent.computeImeTarget(true); mDisplayContent.applySurfaceChangesTransaction(); + app.mAboveInsetsState.getSource(ITYPE_IME).setVisible(true); + app.mAboveInsetsState.getSource(ITYPE_IME).setFrame(mImeWindow.getFrame()); // Make sure app got notified. verify(app, atLeast(1)).notifyInsetsChanged(); @@ -234,6 +220,8 @@ public class InsetsStateControllerTest extends WindowTestsBase { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); final WindowState child = createWindow(app, TYPE_APPLICATION, "child"); + app.mAboveInsetsState.set(getController().getRawInsetsState()); + child.mAboveInsetsState.set(getController().getRawInsetsState()); child.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM; mDisplayContent.computeImeTarget(true); @@ -242,7 +230,8 @@ public class InsetsStateControllerTest extends WindowTestsBase { getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true); assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible()); - assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME).isVisible()); + assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME) + .isVisible()); } @UseTestDisplay(addWindows = W_INPUT_METHOD) @@ -252,6 +241,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); final WindowState child = createWindow(app, TYPE_APPLICATION, "child"); + app.mAboveInsetsState.addSource(getController().getRawInsetsState().peekSource(ITYPE_IME)); child.mAttrs.flags |= FLAG_NOT_FOCUSABLE; child.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); @@ -261,7 +251,8 @@ public class InsetsStateControllerTest extends WindowTestsBase { getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true); assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible()); - assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME).isVisible()); + assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME) + .isVisible()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index 4dce451809f1..409bad416cf8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast; @@ -49,7 +50,6 @@ import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import android.window.TaskSnapshot; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.os.Binder; @@ -59,6 +59,7 @@ import android.platform.test.annotations.Presubmit; import android.util.SparseBooleanArray; import android.view.IRecentsAnimationRunner; import android.view.SurfaceControl; +import android.window.TaskSnapshot; import androidx.test.filters.SmallTest; @@ -99,6 +100,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { when(mMockRunner.asBinder()).thenReturn(new Binder()); mController = spy(new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks, DEFAULT_DISPLAY)); + mController.mShouldAttachNavBarToAppDuringTransition = false; mRootHomeTask = mDefaultDisplay.getDefaultTaskDisplayArea().getRootHomeTask(); assertNotNull(mRootHomeTask); } @@ -499,6 +501,48 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { assertTrue(childTask.isAnimatingByRecents()); } + @Test + public void testRestoreNavBarWhenEnteringRecents_expectAnimation() { + setupForShouldAttachNavBarDuringTransition(); + final ActivityRecord activity = createActivityRecord(mDefaultDisplay); + final ActivityRecord homeActivity = createHomeActivity(); + initializeRecentsAnimationController(mController, homeActivity); + + final WindowToken navToken = mDefaultDisplay.getDisplayPolicy().getNavigationBar().mToken; + final SurfaceControl.Transaction transaction = navToken.getPendingTransaction(); + + verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl()); + + final WindowContainer parent = navToken.getParent(); + final NavBarFadeAnimationController navBarFadeAnimationController = + mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController(); + + mController.cleanupAnimation(REORDER_MOVE_TO_TOP); + verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl()); + verify(navBarFadeAnimationController).fadeWindowToken(true); + } + + @Test + public void testRestoreNavBarWhenBackToApp_expectNoAnimation() { + setupForShouldAttachNavBarDuringTransition(); + final ActivityRecord activity = createActivityRecord(mDefaultDisplay); + final ActivityRecord homeActivity = createHomeActivity(); + initializeRecentsAnimationController(mController, homeActivity); + + final WindowToken navToken = mDefaultDisplay.getDisplayPolicy().getNavigationBar().mToken; + final SurfaceControl.Transaction transaction = navToken.getPendingTransaction(); + + verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl()); + + final WindowContainer parent = navToken.getParent(); + final NavBarFadeAnimationController navBarFadeAnimationController = + mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController(); + + mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION); + verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl()); + verify(navBarFadeAnimationController, never()).fadeWindowToken(anyBoolean()); + } + private ActivityRecord createHomeActivity() { final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService) .setParentTask(mRootHomeTask) @@ -525,6 +569,19 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { assertFalse(activity.mDisplayContent.hasTopFixedRotationLaunchingApp()); } + private void setupForShouldAttachNavBarDuringTransition() { + mController.mShouldAttachNavBarToAppDuringTransition = true; + final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar"); + mDefaultDisplay.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs); + mWm.setRecentsAnimationController(mController); + final NavBarFadeAnimationController mockNavBarFadeAnimationController = + mock(NavBarFadeAnimationController.class); + final DisplayPolicy displayPolicy = spy(mDefaultDisplay.getDisplayPolicy()); + doReturn(displayPolicy).when(mDefaultDisplay).getDisplayPolicy(); + doReturn(mockNavBarFadeAnimationController).when(displayPolicy) + .getNavBarFadeAnimationController(); + } + private static void initializeRecentsAnimationController(RecentsAnimationController controller, ActivityRecord activity) { controller.initialize(activity.getActivityType(), new SparseBooleanArray(), activity); 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 e19024825b38..eb44476a8618 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -16,12 +16,15 @@ package com.android.server.wm; +import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; +import static android.view.SurfaceProto.ROTATION_180; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -39,7 +42,6 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.clearInvocations; @@ -243,7 +245,7 @@ public class SizeCompatTests extends WindowTestsBase { // The bounds should be [800, 0 - 1800, 2500]. assertEquals(origBounds.width(), currentBounds.width()); assertEquals(origBounds.height(), currentBounds.height()); - assertEquals(Configuration.ORIENTATION_LANDSCAPE, display.getConfiguration().orientation); + assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation); assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation); // The previous resize operation doesn't consider the rotation change after size changed. @@ -504,7 +506,7 @@ public class SizeCompatTests extends WindowTestsBase { compatTokens.clear(); // Make the activity resizable again by restarting it - activity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE; + activity.info.resizeMode = RESIZE_MODE_RESIZEABLE; activity.mVisibleRequested = true; activity.restartProcessIfVisible(); // The full lifecycle isn't hooked up so manually set state to resumed @@ -521,7 +523,7 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 2500); // Make the task root resizable. - mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE; + mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE; // Create a size compat activity on the same task. final ActivityRecord activity = new ActivityBuilder(mAtm) @@ -549,7 +551,7 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 2500); // Make the task root resizable. - mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE; + mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE; // Create a size compat activity on the same task. final ActivityRecord activity = new ActivityBuilder(mAtm) @@ -729,7 +731,7 @@ public class SizeCompatTests extends WindowTestsBase { // Update with new activity requested orientation and recompute bounds with no previous // size compat cache. verify(mTask).onDescendantOrientationChanged(same(newActivity)); - verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt()); + verify(mTask).computeFullscreenBounds(any(), any()); final Rect displayBounds = new Rect(display.getBounds()); final Rect taskBounds = new Rect(mTask.getBounds()); @@ -770,7 +772,7 @@ public class SizeCompatTests extends WindowTestsBase { // Update with new activity requested orientation and recompute bounds with no previous // size compat cache. verify(mTask).onDescendantOrientationChanged(same(newActivity)); - verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt()); + verify(mTask).computeFullscreenBounds(any(), any()); final Rect displayBounds = new Rect(display.getBounds()); final Rect taskBounds = new Rect(mTask.getBounds()); @@ -821,6 +823,88 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(activityBounds, mActivity.getBounds()); } + @Test + public void testDisplayIgnoreOrientationRequest_rotated180_notInSizeCompat() { + // Set up a display in landscape and ignoring orientation request. + setUpDisplaySizeWithApp(2800, 1400); + final DisplayContent display = mActivity.mDisplayContent; + display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + // Portrait fixed app. + prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT); + + // In Task letterbox + assertTrue(mTask.isTaskLetterboxed()); + assertFalse(mActivity.inSizeCompatMode()); + + // Rotate display to portrait. + rotateDisplay(display, ROTATION_90); + + // App should be in size compat. + assertFalse(mTask.isTaskLetterboxed()); + assertScaled(); + + // Rotate display to landscape. + rotateDisplay(display, ROTATION_180); + + // In Task letterbox + assertTrue(mTask.isTaskLetterboxed()); + assertFalse(mActivity.inSizeCompatMode()); + } + + @Test + public void testDisplayIgnoreOrientationRequestWithInsets_rotated180_notInSizeCompat() { + // Set up a display in portrait with display cutout and ignoring orientation request. + final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1400, 2800) + .setNotch(75) + .build(); + setUpApp(display); + display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + // Landscape fixed app. + prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_LANDSCAPE); + + // In Task letterbox + assertTrue(mTask.isTaskLetterboxed()); + assertFalse(mActivity.inSizeCompatMode()); + + // Rotate display to portrait. + rotateDisplay(display, ROTATION_90); + + // App should be in size compat. + assertFalse(mTask.isTaskLetterboxed()); + assertScaled(); + + // Rotate display to landscape. + rotateDisplay(display, ROTATION_180); + + // In Task letterbox + assertTrue(mTask.isTaskLetterboxed()); + assertFalse(mActivity.inSizeCompatMode()); + } + + @Test + public void testTaskDisplayAreaNotFillDisplay() { + setUpDisplaySizeWithApp(1400, 2800); + final DisplayContent display = mActivity.mDisplayContent; + final TaskDisplayArea taskDisplayArea = mActivity.getDisplayArea(); + taskDisplayArea.setBounds(0, 0, 1000, 2400); + + // Portrait fixed app. + prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_LANDSCAPE); + + final Rect displayBounds = new Rect(display.getBounds()); + assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation); + assertEquals(2800, displayBounds.width()); + assertEquals(1400, displayBounds.height()); + taskDisplayArea.setBounds(0, 0, 2400, 1000); + + final Rect activityBounds = new Rect(mActivity.getBounds()); + assertFalse(mActivity.inSizeCompatMode()); + assertEquals(2400, activityBounds.width()); + assertEquals(1000, activityBounds.height()); + } + private static WindowState addWindowToActivity(ActivityRecord activity) { final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; @@ -863,13 +947,25 @@ public class SizeCompatTests extends WindowTestsBase { prepareUnresizable(activity, -1 /* maxAspect */, screenOrientation); } - /** - * Setups {@link #mActivity} as a size-compat-mode-able activity with fixed aspect and/or - * orientation. - */ static void prepareUnresizable(ActivityRecord activity, float maxAspect, int screenOrientation) { - activity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE; + prepareLimitedBounds(activity, maxAspect, screenOrientation, true /* isUnresizable */); + } + + static void prepareLimitedBounds(ActivityRecord activity, int screenOrientation, + boolean isUnresizable) { + prepareLimitedBounds(activity, -1 /* maxAspect */, screenOrientation, isUnresizable); + } + + /** + * Setups {@link #mActivity} with restriction on its bounds, such as maxAspect, fixed + * orientation, and/or whether it is resizable. + */ + static void prepareLimitedBounds(ActivityRecord activity, float maxAspect, + int screenOrientation, boolean isUnresizable) { + activity.info.resizeMode = isUnresizable + ? RESIZE_MODE_UNRESIZEABLE + : RESIZE_MODE_RESIZEABLE; activity.mVisibleRequested = true; if (maxAspect >= 0) { activity.info.maxAspectRatio = maxAspect; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java index 1607f013ee81..a1f89ec75784 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java @@ -275,7 +275,7 @@ public class WindowFrameTests extends WindowTestsBase { imeSource.setFrame(imeFrame); imeSource.setVisible(true); w.updateRequestedVisibility(state); - w.mBehindIme = true; + w.mAboveInsetsState.addSource(imeSource); // With no insets or system decor all the frames incoming from PhoneWindowManager // are identical. 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 fe7bdd8f620a..d8be2c1a21a7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -80,6 +80,8 @@ import android.window.WindowContainerTransaction; import androidx.test.filters.SmallTest; +import com.android.server.wm.TaskOrganizerController.PendingTaskEvent; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -150,10 +152,14 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); stack.removeImmediately(); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskVanished(any()); } @@ -162,15 +168,21 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack, false); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, never()) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); stack.setHasBeenVisible(true); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); assertTrue(stack.getHasBeenVisible()); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); stack.removeImmediately(); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskVanished(any()); } @@ -195,12 +207,16 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack, false /* fakeDraw */); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, never()) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTrue(stack.isOrganized()); mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); assertTaskVanished(organizer, false /* expectVanished */, stack); assertFalse(stack.isOrganized()); } @@ -210,11 +226,16 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTrue(stack.isOrganized()); stack.setTaskOrganizer(null); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); + verify(organizer).onTaskVanished(any()); assertFalse(stack.isOrganized()); } @@ -224,11 +245,16 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTrue(stack.isOrganized()); mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); + assertTaskVanished(organizer, true /* expectVanished */, stack); assertFalse(stack.isOrganized()); } @@ -243,6 +269,8 @@ public class WindowOrganizerTests extends WindowTestsBase { final Task task3 = createTask(stack3); final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>(); final ITaskOrganizer organizer = registerMockOrganizer(existingTasks); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); // verify that tasks are returned and taskAppeared is not called assertContainsTasks(existingTasks, stack, stack2, stack3); @@ -254,6 +282,8 @@ public class WindowOrganizerTests extends WindowTestsBase { // Now we replace the registration and verify the new organizer receives existing tasks final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>(); final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); assertContainsTasks(existingTasks2, stack, stack2, stack3); verify(organizer2, times(0)).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); @@ -265,6 +295,8 @@ public class WindowOrganizerTests extends WindowTestsBase { // Now we unregister the second one, the first one should automatically be reregistered // so we verify that it's now seeing changes. mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(3)) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTaskVanished(organizer2, true /* expectVanished */, stack, stack2, stack3); @@ -599,6 +631,8 @@ public class WindowOrganizerTests extends WindowTestsBase { Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask( mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null); RunningTaskInfo info1 = task.getTaskInfo(); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); lastReportedTiles.clear(); called[0] = false; @@ -673,6 +707,8 @@ public class WindowOrganizerTests extends WindowTestsBase { Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null); RunningTaskInfo info2 = task2.getTaskInfo(); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks( mDisplayContent.mDisplayId, null /* activityTypes */).size(); @@ -856,6 +892,8 @@ public class WindowOrganizerTests extends WindowTestsBase { .setAspectRatio(new Rational(3, 4)).build(); mWm.mAtmService.mActivityClientController.setPictureInPictureParams(record.token, p2); waitUntilHandlersIdle(); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); assertNotNull(o.mChangedInfo); assertNotNull(o.mChangedInfo.pictureInPictureParams); final Rational ratio = o.mChangedInfo.pictureInPictureParams.getAspectRatioRational(); @@ -895,16 +933,24 @@ public class WindowOrganizerTests extends WindowTestsBase { stack.setTaskOrganizer(organizer); // setHasBeenVisible was already called once by the set-up code. stack.setHasBeenVisible(true); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(1)) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); stack.setTaskOrganizer(null); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(1)).onTaskVanished(any()); stack.setTaskOrganizer(organizer); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(2)) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); stack.removeImmediately(); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(2)).onTaskVanished(any()); } @@ -923,6 +969,8 @@ public class WindowOrganizerTests extends WindowTestsBase { // Verify a back pressed does not call the organizer mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, never()).onBackPressedOnTaskRoot(any()); // Enable intercepting back @@ -931,6 +979,8 @@ public class WindowOrganizerTests extends WindowTestsBase { // Verify now that the back press does call the organizer mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); // Disable intercepting back @@ -939,6 +989,8 @@ public class WindowOrganizerTests extends WindowTestsBase { // Verify now that the back press no longer calls the organizer mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); } @@ -1019,6 +1071,151 @@ public class WindowOrganizerTests extends WindowTestsBase { assertTrue(task2.isOrganized()); } + @Test + public void testAppearDeferThenInfoChange() { + final ITaskOrganizer organizer = registerMockOrganizer(); + final Task stack = createStack(); + + // Assume layout defer + mWm.mWindowPlacerLocked.deferLayout(); + + final Task task = createTask(stack); + final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + + stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); + waitUntilHandlersIdle(); + + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + assertEquals(1, pendingEvents.size()); + assertEquals(PendingTaskEvent.EVENT_APPEARED, pendingEvents.get(0).mEventType); + assertEquals("TestDescription", + pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); + } + + @Test + public void testAppearDeferThenVanish() { + final ITaskOrganizer organizer = registerMockOrganizer(); + final Task stack = createStack(); + + // Assume layout defer + mWm.mWindowPlacerLocked.deferLayout(); + + final Task task = createTask(stack); + + stack.removeImmediately(); + waitUntilHandlersIdle(); + + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + assertEquals(0, pendingEvents.size()); + } + + @Test + public void testInfoChangeDeferMultiple() { + final ITaskOrganizer organizer = registerMockOrganizer(); + final Task stack = createStack(); + final Task task = createTask(stack); + final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + + // Assume layout defer + mWm.mWindowPlacerLocked.deferLayout(); + + stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); + waitUntilHandlersIdle(); + + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + assertEquals(1, pendingEvents.size()); + assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType); + assertEquals("TestDescription", + pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); + + record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription2")); + waitUntilHandlersIdle(); + + pendingEvents = getTaskPendingEvent(stack); + assertEquals(1, pendingEvents.size()); + assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType); + assertEquals("TestDescription2", + pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); + } + + @Test + public void testInfoChangDeferThenVanish() { + final ITaskOrganizer organizer = registerMockOrganizer(); + final Task stack = createStack(); + final Task task = createTask(stack); + final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + + // Assume layout defer + mWm.mWindowPlacerLocked.deferLayout(); + + stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); + + stack.removeImmediately(); + waitUntilHandlersIdle(); + + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + assertEquals(1, pendingEvents.size()); + assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); + assertEquals("TestDescription", + pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); + } + + @Test + public void testVanishDeferThenInfoChange() { + final ITaskOrganizer organizer = registerMockOrganizer(); + final Task stack = createStack(); + final Task task = createTask(stack); + final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + + // Assume layout defer + mWm.mWindowPlacerLocked.deferLayout(); + + stack.removeImmediately(); + stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + waitUntilHandlersIdle(); + + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + assertEquals(1, pendingEvents.size()); + assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); + } + + @Test + public void testVanishDeferThenBackOnRoot() { + final ITaskOrganizer organizer = registerMockOrganizer(); + final Task stack = createStack(); + final Task task = createTask(stack); + final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + + // Assume layout defer + mWm.mWindowPlacerLocked.deferLayout(); + + stack.removeImmediately(); + mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(record.token); + waitUntilHandlersIdle(); + + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + assertEquals(1, pendingEvents.size()); + assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); + } + + private ArrayList<PendingTaskEvent> getTaskPendingEvent(Task task) { + ArrayList<PendingTaskEvent> total = + mWm.mAtmService.mTaskOrganizerController.getPendingEventList(); + ArrayList<PendingTaskEvent> result = new ArrayList(); + + for (int i = 0; i < total.size(); i++) { + PendingTaskEvent entry = total.get(i); + if (entry.mTask.mTaskId == task.mTaskId) { + result.add(entry); + } + } + + return result; + } + /** * Verifies that task vanished is called for a specific task. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java index 811a14666db9..c82ba995f12e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java @@ -35,11 +35,19 @@ import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.WindowStateAnimator.PRESERVED_SURFACE_LAYER; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.os.Binder; import android.platform.test.annotations.Presubmit; +import android.util.SparseBooleanArray; +import android.view.IRecentsAnimationRunner; import android.view.SurfaceControl; import android.view.SurfaceSession; @@ -451,4 +459,38 @@ public class ZOrderingTests extends WindowTestsBase { assertWindowHigher(mDockedDividerWindow, splitScreenSecondaryWindow); assertWindowHigher(pinnedStackWindow, mDockedDividerWindow); } + + @Test + public void testAttachNavBarWhenEnteringRecents_expectNavBarHigherThanIme() { + // create RecentsAnimationController + IRecentsAnimationRunner mockRunner = mock(IRecentsAnimationRunner.class); + when(mockRunner.asBinder()).thenReturn(new Binder()); + final int displayId = mDisplayContent.getDisplayId(); + RecentsAnimationController controller = new RecentsAnimationController( + mWm, mockRunner, null, displayId); + spyOn(controller); + controller.mShouldAttachNavBarToAppDuringTransition = true; + doReturn(mNavBarWindow.mToken).when(controller).getNavigationBarWindowToken(); + mWm.setRecentsAnimationController(controller); + + // set ime visible + spyOn(mDisplayContent.mInputMethodWindow); + doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible(); + + // create home activity + Task rootHomeTask = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask(); + final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService) + .setParentTask(rootHomeTask) + .setCreateTask(true) + .build(); + homeActivity.setVisibility(true); + + // start recent animation + controller.initialize(homeActivity.getActivityType(), new SparseBooleanArray(), + homeActivity); + + mDisplayContent.assignChildLayers(mTransaction); + assertZOrderGreaterThan(mTransaction, mNavBarWindow.mToken.getSurfaceControl(), + mDisplayContent.getImeContainer().getSurfaceControl()); + } } diff --git a/services/translation/java/com/android/server/translation/TranslationManagerService.java b/services/translation/java/com/android/server/translation/TranslationManagerService.java index e2aabe6a89ea..84c6e7be16be 100644 --- a/services/translation/java/com/android/server/translation/TranslationManagerService.java +++ b/services/translation/java/com/android/server/translation/TranslationManagerService.java @@ -22,13 +22,17 @@ import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_FAIL; import android.content.Context; import android.os.RemoteException; import android.util.Slog; +import android.view.autofill.AutofillId; import android.view.translation.ITranslationManager; import android.view.translation.TranslationSpec; +import android.view.translation.UiTranslationManager.UiTranslationState; import com.android.internal.os.IResultReceiver; import com.android.server.infra.AbstractMasterSystemService; import com.android.server.infra.FrameworkResourcesServiceNameResolver; +import java.util.List; + /** * Entry point service for translation management. * @@ -82,6 +86,19 @@ public final class TranslationManagerService } } } + + @Override + public void updateUiTranslationState(@UiTranslationState int state, + TranslationSpec sourceSpec, TranslationSpec destSpec, List<AutofillId> viewIds, + int taskId, int userId) { + synchronized (mLock) { + final TranslationManagerServiceImpl service = getServiceForUserLocked(userId); + if (service != null) { + service.updateUiTranslationState(state, sourceSpec, destSpec, viewIds, + taskId); + } + } + } } @Override // from SystemService diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java index b1f6f80d4158..5b1074fc4c0a 100644 --- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java +++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java @@ -26,7 +26,9 @@ import android.content.pm.ServiceInfo; import android.os.RemoteException; import android.service.translation.TranslationServiceInfo; import android.util.Slog; +import android.view.autofill.AutofillId; import android.view.translation.TranslationSpec; +import android.view.translation.UiTranslationManager.UiTranslationState; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.IResultReceiver; @@ -34,6 +36,7 @@ import com.android.internal.util.SyncResultReceiver; import com.android.server.infra.AbstractPerUserSystemService; import java.util.ArrayList; +import java.util.List; final class TranslationManagerServiceImpl extends AbstractPerUserSystemService<TranslationManagerServiceImpl, TranslationManagerService> { @@ -122,4 +125,11 @@ final class TranslationManagerServiceImpl extends remoteService.onSessionCreated(sourceSpec, destSpec, sessionId, resultReceiver); } } + + @GuardedBy("mLock") + public void updateUiTranslationState(@UiTranslationState int state, + TranslationSpec sourceSpec, TranslationSpec destSpec, List<AutofillId> viewIds, + int taskId) { + // TODO: implement this in next change + } } diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java index fd462c2e6dc2..bfb159f766d5 100644 --- a/services/usage/java/com/android/server/usage/IntervalStats.java +++ b/services/usage/java/com/android/server/usage/IntervalStats.java @@ -54,6 +54,7 @@ import android.util.proto.ProtoInputStream; import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; +import java.util.Arrays; import java.util.List; public class IntervalStats { @@ -459,13 +460,14 @@ public class IntervalStats { */ private boolean deobfuscateUsageStats(PackagesTokenData packagesTokenData) { boolean dataOmitted = false; + final ArraySet<Integer> omittedTokens = new ArraySet<>(); final int usageStatsSize = packageStatsObfuscated.size(); for (int statsIndex = 0; statsIndex < usageStatsSize; statsIndex++) { final int packageToken = packageStatsObfuscated.keyAt(statsIndex); final UsageStats usageStats = packageStatsObfuscated.valueAt(statsIndex); usageStats.mPackageName = packagesTokenData.getPackageString(packageToken); if (usageStats.mPackageName == null) { - Slog.e(TAG, "Unable to parse usage stats package " + packageToken); + omittedTokens.add(packageToken); dataOmitted = true; continue; } @@ -477,8 +479,6 @@ public class IntervalStats { final int actionToken = usageStats.mChooserCountsObfuscated.keyAt(actionIndex); final String action = packagesTokenData.getString(packageToken, actionToken); if (action == null) { - Slog.i(TAG, "Unable to parse chooser action " + actionToken - + " for package " + packageToken); continue; } final SparseIntArray categoryCounts = @@ -489,8 +489,6 @@ public class IntervalStats { final String category = packagesTokenData.getString(packageToken, categoryToken); if (category == null) { - Slog.i(TAG, "Unable to parse chooser category " + categoryToken - + " for package " + packageToken); continue; } categoryCountsMap.put(category, categoryCounts.valueAt(categoryIndex)); @@ -499,6 +497,10 @@ public class IntervalStats { } packageStats.put(usageStats.mPackageName, usageStats); } + if (dataOmitted) { + Slog.d(TAG, "Unable to parse usage stats packages: " + + Arrays.toString(omittedTokens.toArray())); + } return dataOmitted; } @@ -511,12 +513,13 @@ public class IntervalStats { */ private boolean deobfuscateEvents(PackagesTokenData packagesTokenData) { boolean dataOmitted = false; + final ArraySet<Integer> omittedTokens = new ArraySet<>(); for (int i = this.events.size() - 1; i >= 0; i--) { final Event event = this.events.get(i); final int packageToken = event.mPackageToken; event.mPackage = packagesTokenData.getPackageString(packageToken); if (event.mPackage == null) { - Slog.e(TAG, "Unable to parse event package " + packageToken); + omittedTokens.add(packageToken); this.events.remove(i); dataOmitted = true; continue; @@ -524,26 +527,14 @@ public class IntervalStats { if (event.mClassToken != PackagesTokenData.UNASSIGNED_TOKEN) { event.mClass = packagesTokenData.getString(packageToken, event.mClassToken); - if (event.mClass == null) { - Slog.i(TAG, "Unable to parse class " + event.mClassToken - + " for package " + packageToken); - } } if (event.mTaskRootPackageToken != PackagesTokenData.UNASSIGNED_TOKEN) { event.mTaskRootPackage = packagesTokenData.getString(packageToken, event.mTaskRootPackageToken); - if (event.mTaskRootPackage == null) { - Slog.i(TAG, "Unable to parse task root package " + event.mTaskRootPackageToken - + " for package " + packageToken); - } } if (event.mTaskRootClassToken != PackagesTokenData.UNASSIGNED_TOKEN) { event.mTaskRootClass = packagesTokenData.getString(packageToken, event.mTaskRootClassToken); - if (event.mTaskRootClass == null) { - Slog.i(TAG, "Unable to parse task root class " + event.mTaskRootClassToken - + " for package " + packageToken); - } } switch (event.mEventType) { case CONFIGURATION_CHANGE: @@ -555,7 +546,7 @@ public class IntervalStats { event.mShortcutId = packagesTokenData.getString(packageToken, event.mShortcutIdToken); if (event.mShortcutId == null) { - Slog.e(TAG, "Unable to parse shortcut " + event.mShortcutIdToken + Slog.v(TAG, "Unable to parse shortcut " + event.mShortcutIdToken + " for package " + packageToken); this.events.remove(i); dataOmitted = true; @@ -566,7 +557,7 @@ public class IntervalStats { event.mNotificationChannelId = packagesTokenData.getString(packageToken, event.mNotificationChannelIdToken); if (event.mNotificationChannelId == null) { - Slog.e(TAG, "Unable to parse notification channel " + Slog.v(TAG, "Unable to parse notification channel " + event.mNotificationChannelIdToken + " for package " + packageToken); this.events.remove(i); @@ -577,7 +568,7 @@ public class IntervalStats { case LOCUS_ID_SET: event.mLocusId = packagesTokenData.getString(packageToken, event.mLocusIdToken); if (event.mLocusId == null) { - Slog.e(TAG, "Unable to parse locus " + event.mLocusIdToken + Slog.v(TAG, "Unable to parse locus " + event.mLocusIdToken + " for package " + packageToken); this.events.remove(i); dataOmitted = true; @@ -586,6 +577,10 @@ public class IntervalStats { break; } } + if (dataOmitted) { + Slog.d(TAG, "Unable to parse event packages: " + + Arrays.toString(omittedTokens.toArray())); + } return dataOmitted; } diff --git a/services/usage/java/com/android/server/usage/PackagesTokenData.java b/services/usage/java/com/android/server/usage/PackagesTokenData.java index f19abbbac485..272daf4784f9 100644 --- a/services/usage/java/com/android/server/usage/PackagesTokenData.java +++ b/services/usage/java/com/android/server/usage/PackagesTokenData.java @@ -16,6 +16,7 @@ package com.android.server.usage; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; @@ -56,6 +57,11 @@ public final class PackagesTokenData { * Stores a map of packages that were removed and when they were removed. */ public final ArrayMap<String, Long> removedPackagesMap = new ArrayMap<>(); + /** + * Stores a set of removed package tokens. This is solely for dump purposes when comparing + * parsing errors to recently removed packages. + */ + public final ArraySet<Integer> removedPackageTokens = new ArraySet<>(); public PackagesTokenData() { } @@ -174,6 +180,7 @@ public final class PackagesTokenData { final int packageToken = packagesToTokensMap.get(packageName).get(packageName); packagesToTokensMap.remove(packageName); tokensToPackagesMap.delete(packageToken); + removedPackageTokens.add(packageToken); return packageToken; } } diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java index 52b0afb56b3c..a0a390947822 100644 --- a/services/usage/java/com/android/server/usage/StorageStatsService.java +++ b/services/usage/java/com/android/server/usage/StorageStatsService.java @@ -514,6 +514,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub { res.codeBytes = stats.codeSize + stats.externalCodeSize; res.dataBytes = stats.dataSize + stats.externalDataSize; res.cacheBytes = stats.cacheSize + stats.externalCacheSize; + res.externalCacheBytes = stats.externalCacheSize; return res; } diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java index 9d48955c87be..a4f5249c9cb4 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java +++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java @@ -53,6 +53,7 @@ import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -1500,6 +1501,10 @@ public class UsageStatsDatabase { pw.increaseIndent(); pw.println("Counter: " + mPackagesTokenData.counter); pw.println("Tokens Map Size: " + mPackagesTokenData.tokensToPackagesMap.size()); + if (!mPackagesTokenData.removedPackageTokens.isEmpty()) { + pw.println("Removed Package Tokens: " + + Arrays.toString(mPackagesTokenData.removedPackageTokens.toArray())); + } for (int i = 0; i < mPackagesTokenData.tokensToPackagesMap.size(); i++) { final int packageToken = mPackagesTokenData.tokensToPackagesMap.keyAt(i); final String packageStrings = String.join(", ", diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index f1bea676ed20..717040adfac9 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -18,6 +18,7 @@ package android.telecom; import static android.Manifest.permission.MODIFY_PHONE_STATE; +import android.Manifest; import android.annotation.ElapsedRealtimeLong; import android.annotation.IntDef; import android.annotation.IntRange; @@ -109,6 +110,20 @@ import java.util.concurrent.ConcurrentHashMap; */ public abstract class Connection extends Conferenceable { + /**@hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "STATE_", value = { + STATE_INITIALIZING, + STATE_NEW, + STATE_RINGING, + STATE_DIALING, + STATE_ACTIVE, + STATE_HOLDING, + STATE_DISCONNECTED, + STATE_PULLING_CALL + }) + public @interface ConnectionState {} + /** * The connection is initializing. This is generally the first state for a {@code Connection} * returned by a {@link ConnectionService}. @@ -3343,6 +3358,24 @@ public abstract class Connection extends Conferenceable { */ public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {} + /** + * Indicates that call filtering in Telecom is complete + * + * This method is called for a connection created via + * {@link ConnectionService#onCreateIncomingConnection} when call filtering completes in + * Telecom, including checking the blocked number db, per-contact settings, and custom call + * filtering apps. + * + * @param isBlocked {@code true} if the call was blocked, {@code false} otherwise. If this is + * {@code true}, {@link #onDisconnect()} will be called soon after + * this is called. + * @param isInContacts Indicates whether the caller is in the user's contacts list. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_CONTACTS) + public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts) { } + static String toLogSafePhoneNumber(String number) { // For unknown number, log empty string. if (number == null) { diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index b1ccb533e83d..f86f9d5dad3d 100755 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -147,6 +147,7 @@ public abstract class ConnectionService extends Service { private static final String SESSION_POST_DIAL_CONT = "CS.oPDC"; private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC"; private static final String SESSION_SEND_CALL_EVENT = "CS.sCE"; + private static final String SESSION_CALL_FILTERING_COMPLETED = "CS.oCFC"; private static final String SESSION_HANDOVER_COMPLETE = "CS.hC"; private static final String SESSION_EXTRAS_CHANGED = "CS.oEC"; private static final String SESSION_START_RTT = "CS.+RTT"; @@ -200,6 +201,7 @@ public abstract class ConnectionService extends Service { private static final int MSG_ADD_PARTICIPANT = 39; private static final int MSG_EXPLICIT_CALL_TRANSFER = 40; private static final int MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE = 41; + private static final int MSG_ON_CALL_FILTERING_COMPLETED = 42; private static Connection sNullConnection; @@ -722,6 +724,22 @@ public abstract class ConnectionService extends Service { } @Override + public void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts, + Session.Info sessionInfo) { + Log.startSession(sessionInfo, SESSION_CALL_FILTERING_COMPLETED); + try { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = isBlocked; + args.arg3 = isInContacts; + args.arg4 = Log.createSubsession(); + mHandler.obtainMessage(MSG_ON_CALL_FILTERING_COMPLETED, args).sendToTarget(); + } finally { + Log.endSession(); + } + } + + @Override public void onExtrasChanged(String callId, Bundle extras, Session.Info sessionInfo) { Log.startSession(sessionInfo, SESSION_EXTRAS_CHANGED); try { @@ -1354,6 +1372,21 @@ public abstract class ConnectionService extends Service { } break; } + case MSG_ON_CALL_FILTERING_COMPLETED: { + SomeArgs args = (SomeArgs) msg.obj; + try { + Log.continueSession((Session) args.arg4, + SESSION_HANDLER + SESSION_CALL_FILTERING_COMPLETED); + String callId = (String) args.arg1; + boolean isBlocked = (boolean) args.arg2; + boolean isInContacts = (boolean) args.arg3; + onCallFilteringCompleted(callId, isBlocked, isInContacts); + } finally { + args.recycle(); + Log.endSession(); + } + break; + } case MSG_HANDOVER_COMPLETE: { SomeArgs args = (SomeArgs) msg.obj; try { @@ -2345,6 +2378,14 @@ public abstract class ConnectionService extends Service { } } + private void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts) { + Log.i(this, "onCallFilteringCompleted(%s, %b, %b)", isBlocked, isInContacts); + Connection connection = findConnectionForAction(callId, "onCallFilteringCompleted"); + if (connection != null) { + connection.onCallFilteringCompleted(isBlocked, isInContacts); + } + } + /** * Notifies a {@link Connection} that a handover has completed. * diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java index 52210a55c8d0..feb2ca53bbbe 100644 --- a/telecomm/java/android/telecom/RemoteConnection.java +++ b/telecomm/java/android/telecom/RemoteConnection.java @@ -16,8 +16,10 @@ package android.telecom; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.hardware.camera2.CameraManager; import android.net.Uri; @@ -1198,6 +1200,28 @@ public final class RemoteConnection { } /** + * Notifies this {@link RemoteConnection} that call filtering has completed, as well as + * the results of a contacts lookup for the remote party. + * @param isBlocked Whether call filtering indicates that the call should be blocked + * @param isInContacts Whether the remote party is in the user's contacts + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_CONTACTS) + public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts) { + Log.startSession("RC.oCFC", getActiveOwnerInfo()); + try { + if (mConnected) { + mConnectionService.onCallFilteringCompleted(mConnectionId, isBlocked, isInContacts, + null /*Session.Info*/); + } + } catch (RemoteException ignored) { + } finally { + Log.endSession(); + } + } + + /** * Notifies this {@link RemoteConnection} of a response to a previous remotely-initiated RTT * upgrade request sent via {@link Connection#sendRemoteRttRequest}. * Acceptance of the request is indicated by the supplied {@link RttTextStream} being non-null, diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl index fb5417994b57..92264be5fa90 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl @@ -118,6 +118,9 @@ oneway interface IConnectionService { void sendCallEvent(String callId, String event, in Bundle extras, in Session.Info sessionInfo); + void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts, + in Session.Info sessionInfo); + void onExtrasChanged(String callId, in Bundle extras, in Session.Info sessionInfo); void startRtt(String callId, in ParcelFileDescriptor fromInCall, diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java index d01297147fdb..f6d18fcd9ab3 100644 --- a/telephony/java/android/telephony/AccessNetworkConstants.java +++ b/telephony/java/android/telephony/AccessNetworkConstants.java @@ -18,10 +18,7 @@ package android.telephony; import android.annotation.IntDef; import android.annotation.SystemApi; -import android.hardware.radio.V1_1.GeranBands; import android.hardware.radio.V1_5.AccessNetwork; -import android.hardware.radio.V1_5.EutranBands; -import android.hardware.radio.V1_5.UtranBands; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -117,52 +114,120 @@ public final class AccessNetworkConstants { * http://www.etsi.org/deliver/etsi_ts/145000_145099/145005/14.00.00_60/ts_145005v140000p.pdf */ public static final class GeranBand { - public static final int BAND_T380 = GeranBands.BAND_T380; - public static final int BAND_T410 = GeranBands.BAND_T410; - public static final int BAND_450 = GeranBands.BAND_450; - public static final int BAND_480 = GeranBands.BAND_480; - public static final int BAND_710 = GeranBands.BAND_710; - public static final int BAND_750 = GeranBands.BAND_750; - public static final int BAND_T810 = GeranBands.BAND_T810; - public static final int BAND_850 = GeranBands.BAND_850; - public static final int BAND_P900 = GeranBands.BAND_P900; - public static final int BAND_E900 = GeranBands.BAND_E900; - public static final int BAND_R900 = GeranBands.BAND_R900; - public static final int BAND_DCS1800 = GeranBands.BAND_DCS1800; - public static final int BAND_PCS1900 = GeranBands.BAND_PCS1900; - public static final int BAND_ER900 = GeranBands.BAND_ER900; + public static final int BAND_T380 = android.hardware.radio.V1_1.GeranBands.BAND_T380; + public static final int BAND_T410 = android.hardware.radio.V1_1.GeranBands.BAND_T410; + public static final int BAND_450 = android.hardware.radio.V1_1.GeranBands.BAND_450; + public static final int BAND_480 = android.hardware.radio.V1_1.GeranBands.BAND_480; + public static final int BAND_710 = android.hardware.radio.V1_1.GeranBands.BAND_710; + public static final int BAND_750 = android.hardware.radio.V1_1.GeranBands.BAND_750; + public static final int BAND_T810 = android.hardware.radio.V1_1.GeranBands.BAND_T810; + public static final int BAND_850 = android.hardware.radio.V1_1.GeranBands.BAND_850; + public static final int BAND_P900 = android.hardware.radio.V1_1.GeranBands.BAND_P900; + public static final int BAND_E900 = android.hardware.radio.V1_1.GeranBands.BAND_E900; + public static final int BAND_R900 = android.hardware.radio.V1_1.GeranBands.BAND_R900; + public static final int BAND_DCS1800 = android.hardware.radio.V1_1.GeranBands.BAND_DCS1800; + public static final int BAND_PCS1900 = android.hardware.radio.V1_1.GeranBands.BAND_PCS1900; + public static final int BAND_ER900 = android.hardware.radio.V1_1.GeranBands.BAND_ER900; + + /** + * GeranBand + * + * @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"BAND_"}, + value = {BAND_T380, + BAND_T410, + BAND_450, + BAND_480, + BAND_710, + BAND_750, + BAND_T810, + BAND_850, + BAND_P900, + BAND_E900, + BAND_R900, + BAND_DCS1800, + BAND_PCS1900, + BAND_ER900}) + + public @interface GeranBands {} /** @hide */ private GeranBand() {} } /** + * 3GPP TS 45.005 Table 2-1 Dynamically mapped ARFCN. + * 3GPP TS 45.005 Table 2-2 Fixed designation of ARFCN. + * @hide + */ + enum GeranBandArfcnFrequency { + + // Dynamically mapped ARFCN +// GERAN_ARFCN_FREQUENCY_BAND_T380(GeranBand.BAND_T380, 380.2, 0), +// GERAN_ARFCN_FREQUENCY_BAND_T410(GeranBand.BAND_T410, 410.2, 0), +// GERAN_ARFCN_FREQUENCY_BAND_710(GeranBand.BAND_710, 698, 0), +// GERAN_ARFCN_FREQUENCY_BAND_750(GeranBand.BAND_750, 747, 438, 30), +// GERAN_ARFCN_FREQUENCY_BAND_T810(GeranBand.BAND_T810, 806, 350), + // Fixed designation of ARFCN + GERAN_ARFCN_FREQUENCY_BAND_450(GeranBand.BAND_450, 450600, 259, 259, 293, 10), + GERAN_ARFCN_FREQUENCY_BAND_480(GeranBand.BAND_480, 479000, 306, 306, 340, 10), + GERAN_ARFCN_FREQUENCY_BAND_850(GeranBand.BAND_850, 824200, 128, 128, 251, 45), + GERAN_ARFCN_FREQUENCY_BAND_DCS1800(GeranBand.BAND_DCS1800, 1710200, 512, 512, 885, 95), + GERAN_ARFCN_FREQUENCY_BAND_PCS1900(GeranBand.BAND_PCS1900, 1850200, 512, 512, 810, 80), + GERAN_ARFCN_FREQUENCY_BAND_E900_1(GeranBand.BAND_E900, 890000, 0, 0, 124, 45), + GERAN_ARFCN_FREQUENCY_BAND_E900_2(GeranBand.BAND_E900, 890000, 1024, 975, 1023, 45), + GERAN_ARFCN_FREQUENCY_BAND_R900_1(GeranBand.BAND_R900, 890000, 0, 0, 124, 45), + GERAN_ARFCN_FREQUENCY_BAND_R900_2(GeranBand.BAND_R900, 890000, 1024, 955, 1023, 45), + GERAN_ARFCN_FREQUENCY_BAND_P900(GeranBand.BAND_P900, 890000, 0, 1, 124, 45), + GERAN_ARFCN_FREQUENCY_BAND_ER900_1(GeranBand.BAND_ER900, 890000, 0, 0, 124, 45), + GERAN_ARFCN_FREQUENCY_BAND_ER900_2(GeranBand.BAND_ER900, 890000, 1024, 940, 1023, 1024); + + GeranBandArfcnFrequency(int band, int uplinkFrequencyFirstKhz, int arfcnOffset, + int arfcnRangeFirst, int arfcnRangeLast, int downlinkOffset) { + this.band = band; + this.uplinkFrequencyFirst = uplinkFrequencyFirstKhz; + this.arfcnOffset = arfcnOffset; + this.arfcnRangeFirst = arfcnRangeFirst; + this.arfcnRangeLast = arfcnRangeLast; + this.downlinkOffset = downlinkOffset; + } + + int band; + int uplinkFrequencyFirst; + int arfcnOffset; + int arfcnRangeFirst; + int arfcnRangeLast; + int downlinkOffset; + } + + /** * Frequency bands for UTRAN. * http://www.etsi.org/deliver/etsi_ts/125100_125199/125104/13.03.00_60/ts_125104v130p.pdf */ public static final class UtranBand { - public static final int BAND_1 = UtranBands.BAND_1; - public static final int BAND_2 = UtranBands.BAND_2; - public static final int BAND_3 = UtranBands.BAND_3; - public static final int BAND_4 = UtranBands.BAND_4; - public static final int BAND_5 = UtranBands.BAND_5; - public static final int BAND_6 = UtranBands.BAND_6; - public static final int BAND_7 = UtranBands.BAND_7; - public static final int BAND_8 = UtranBands.BAND_8; - public static final int BAND_9 = UtranBands.BAND_9; - public static final int BAND_10 = UtranBands.BAND_10; - public static final int BAND_11 = UtranBands.BAND_11; - public static final int BAND_12 = UtranBands.BAND_12; - public static final int BAND_13 = UtranBands.BAND_13; - public static final int BAND_14 = UtranBands.BAND_14; + public static final int BAND_1 = android.hardware.radio.V1_5.UtranBands.BAND_1; + public static final int BAND_2 = android.hardware.radio.V1_5.UtranBands.BAND_2; + public static final int BAND_3 = android.hardware.radio.V1_5.UtranBands.BAND_3; + public static final int BAND_4 = android.hardware.radio.V1_5.UtranBands.BAND_4; + public static final int BAND_5 = android.hardware.radio.V1_5.UtranBands.BAND_5; + public static final int BAND_6 = android.hardware.radio.V1_5.UtranBands.BAND_6; + public static final int BAND_7 = android.hardware.radio.V1_5.UtranBands.BAND_7; + public static final int BAND_8 = android.hardware.radio.V1_5.UtranBands.BAND_8; + public static final int BAND_9 = android.hardware.radio.V1_5.UtranBands.BAND_9; + public static final int BAND_10 = android.hardware.radio.V1_5.UtranBands.BAND_10; + public static final int BAND_11 = android.hardware.radio.V1_5.UtranBands.BAND_11; + public static final int BAND_12 = android.hardware.radio.V1_5.UtranBands.BAND_12; + public static final int BAND_13 = android.hardware.radio.V1_5.UtranBands.BAND_13; + public static final int BAND_14 = android.hardware.radio.V1_5.UtranBands.BAND_14; // band 15, 16, 17, 18 are reserved - public static final int BAND_19 = UtranBands.BAND_19; - public static final int BAND_20 = UtranBands.BAND_20; - public static final int BAND_21 = UtranBands.BAND_21; - public static final int BAND_22 = UtranBands.BAND_22; + public static final int BAND_19 = android.hardware.radio.V1_5.UtranBands.BAND_19; + public static final int BAND_20 = android.hardware.radio.V1_5.UtranBands.BAND_20; + public static final int BAND_21 = android.hardware.radio.V1_5.UtranBands.BAND_21; + public static final int BAND_22 = android.hardware.radio.V1_5.UtranBands.BAND_22; // band 23, 24 are reserved - public static final int BAND_25 = UtranBands.BAND_25; - public static final int BAND_26 = UtranBands.BAND_26; + public static final int BAND_25 = android.hardware.radio.V1_5.UtranBands.BAND_25; + public static final int BAND_26 = android.hardware.radio.V1_5.UtranBands.BAND_26; // Frequency bands for TD-SCDMA. Defined in 3GPP TS 25.102, Table 5.2. @@ -171,115 +236,423 @@ public final class AccessNetworkConstants { * 1900 - 1920 MHz: Uplink and downlink transmission * 2010 - 2025 MHz: Uplink and downlink transmission */ - public static final int BAND_A = UtranBands.BAND_A; + public static final int BAND_A = android.hardware.radio.V1_5.UtranBands.BAND_A; /** * Band B * 1850 - 1910 MHz: Uplink and downlink transmission * 1930 - 1990 MHz: Uplink and downlink transmission */ - public static final int BAND_B = UtranBands.BAND_B; + public static final int BAND_B = android.hardware.radio.V1_5.UtranBands.BAND_B; /** * Band C * 1910 - 1930 MHz: Uplink and downlink transmission */ - public static final int BAND_C = UtranBands.BAND_C; + public static final int BAND_C = android.hardware.radio.V1_5.UtranBands.BAND_C; /** * Band D * 2570 - 2620 MHz: Uplink and downlink transmission */ - public static final int BAND_D = UtranBands.BAND_D; + public static final int BAND_D = android.hardware.radio.V1_5.UtranBands.BAND_D; /** * Band E * 2300—2400 MHz: Uplink and downlink transmission */ - public static final int BAND_E = UtranBands.BAND_E; + public static final int BAND_E = android.hardware.radio.V1_5.UtranBands.BAND_E; /** * Band F * 1880 - 1920 MHz: Uplink and downlink transmission */ - public static final int BAND_F = UtranBands.BAND_F; + public static final int BAND_F = android.hardware.radio.V1_5.UtranBands.BAND_F; + + /** + * UtranBand + * + * @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"BAND_"}, + value = {BAND_1, + BAND_2, + BAND_3, + BAND_4, + BAND_5, + BAND_6, + BAND_7, + BAND_8, + BAND_9, + BAND_10, + BAND_11, + BAND_12, + BAND_13, + BAND_14, + BAND_19, + BAND_20, + BAND_21, + BAND_22, + BAND_25, + BAND_26, + BAND_A, + BAND_B, + BAND_C, + BAND_D, + BAND_E, + BAND_F}) + + public @interface UtranBands {} /** @hide */ private UtranBand() {} } /** + * 3GPP TS 25.101, Table 5.1 UARFCN definition (general) + * 3GPP TS 25.102, Table 5.2 UTRA Absolute Radio Frequency Channel Number 1.28 Mcps TDD Option. + * + * @hide + */ + enum UtranBandArfcnFrequency { + + UTRAN_ARFCN_FREQUENCY_BAND_1(UtranBand.BAND_1, 0, 10562, 10838, 0, 9612, 9888), + UTRAN_ARFCN_FREQUENCY_BAND_2(UtranBand.BAND_2, 0, 9662, 9938, 0, 9262, 9538), + UTRAN_ARFCN_FREQUENCY_BAND_3(UtranBand.BAND_3, 1575000, 1162, 1513, 1525000, 937, 1288), + UTRAN_ARFCN_FREQUENCY_BAND_4(UtranBand.BAND_4, 1805000, 1537, 1738, 1450000, 1312, 1513), + UTRAN_ARFCN_FREQUENCY_BAND_5(UtranBand.BAND_5, 0, 4357, 4458, 0, 4132, 4233), + UTRAN_ARFCN_FREQUENCY_BAND_6(UtranBand.BAND_6, 0, 4387, 4413, 0, 4162, 4188), + UTRAN_ARFCN_FREQUENCY_BAND_7(UtranBand.BAND_7, 2175000, 2237, 2563, 2100000, 2012, 2338), + UTRAN_ARFCN_FREQUENCY_BAND_8(UtranBand.BAND_8, 340000, 2937, 3088, 340000, 2712, 2863), + UTRAN_ARFCN_FREQUENCY_BAND_9(UtranBand.BAND_9, 0, 9327, 9837, 0, 8762, 8912), + UTRAN_ARFCN_FREQUENCY_BAND_10(UtranBand.BAND_10, 1490000, 3112, 3388, 1135000, 2887, 3163), + UTRAN_ARFCN_FREQUENCY_BAND_11(UtranBand.BAND_11, 736000, 3712, 3787, 733000, 3487, 3562), + UTRAN_ARFCN_FREQUENCY_BAND_12(UtranBand.BAND_12, -37000, 3842, 3903, -22000, 3617, 3678), + UTRAN_ARFCN_FREQUENCY_BAND_13(UtranBand.BAND_13, -55000, 4017, 4043, 21000, 3792, 3818), + UTRAN_ARFCN_FREQUENCY_BAND_14(UtranBand.BAND_14, -63000, 4117, 4143, 12000, 3892, 3918), + UTRAN_ARFCN_FREQUENCY_BAND_19(UtranBand.BAND_19, 735000, 712, 763, 770000, 312, 363), + UTRAN_ARFCN_FREQUENCY_BAND_20(UtranBand.BAND_20, -109000, 4512, 4638, -23000, 4287, 4413), + UTRAN_ARFCN_FREQUENCY_BAND_21(UtranBand.BAND_21, 1326000, 862, 912, 1358000, 462, 512), + UTRAN_ARFCN_FREQUENCY_BAND_22(UtranBand.BAND_22, 2580000, 4662, 5038, 2525000, 4437, 4813), + UTRAN_ARFCN_FREQUENCY_BAND_25(UtranBand.BAND_25, 910000, 5112, 5413, 875000, 4887, 5188), + UTRAN_ARFCN_FREQUENCY_BAND_A(UtranBand.BAND_A, 0, 10054, 10121, 0, 9504, 9596), + UTRAN_ARFCN_FREQUENCY_BAND_B(UtranBand.BAND_B, 0, 9654, 9946, 0, 9254, 9546), + UTRAN_ARFCN_FREQUENCY_BAND_C(UtranBand.BAND_C, 0, 0, 0, 0, 9554, 9646), + UTRAN_ARFCN_FREQUENCY_BAND_D(UtranBand.BAND_D, 0, 0, 0, 0, 12854, 13096), + UTRAN_ARFCN_FREQUENCY_BAND_E(UtranBand.BAND_E, 0, 0, 0, 0, 11504, 11996), + UTRAN_ARFCN_FREQUENCY_BAND_F(UtranBand.BAND_F, 0, 0, 0, 0, 9404, 9596); + + UtranBandArfcnFrequency(int band, int downlinkOffsetKhz, int downlinkRangeFirst, + int downlinkRangeLast, int uplinkOffsetKhz, int uplinkRangeFirst, + int uplinkRangeLast) { + this.band = band; + this.downlinkOffset = downlinkOffsetKhz; + this.downlinkRangeFirst = downlinkRangeFirst; + this.downlinkRangeLast = downlinkRangeLast; + this.uplinkOffset = uplinkOffsetKhz; + this.uplinkRangeFirst = uplinkRangeFirst; + this.uplinkRangeLast = uplinkRangeLast; + } + + int band; + int downlinkOffset; + int downlinkRangeFirst; + int downlinkRangeLast; + int uplinkOffset; + int uplinkRangeFirst; + int uplinkRangeLast; + } + + /** * Frequency bands for EUTRAN. * 3GPP TS 36.101, Version 16.4.0, Table 5.5: Operating bands * https://www.etsi.org/deliver/etsi_ts/136100_136199/136101/15.09.00_60/ts_136101v150900p.pdf */ public static final class EutranBand { - public static final int BAND_1 = EutranBands.BAND_1; - public static final int BAND_2 = EutranBands.BAND_2; - public static final int BAND_3 = EutranBands.BAND_3; - public static final int BAND_4 = EutranBands.BAND_4; - public static final int BAND_5 = EutranBands.BAND_5; - public static final int BAND_6 = EutranBands.BAND_6; - public static final int BAND_7 = EutranBands.BAND_7; - public static final int BAND_8 = EutranBands.BAND_8; - public static final int BAND_9 = EutranBands.BAND_9; - public static final int BAND_10 = EutranBands.BAND_10; - public static final int BAND_11 = EutranBands.BAND_11; - public static final int BAND_12 = EutranBands.BAND_12; - public static final int BAND_13 = EutranBands.BAND_13; - public static final int BAND_14 = EutranBands.BAND_14; - public static final int BAND_17 = EutranBands.BAND_17; - public static final int BAND_18 = EutranBands.BAND_18; - public static final int BAND_19 = EutranBands.BAND_19; - public static final int BAND_20 = EutranBands.BAND_20; - public static final int BAND_21 = EutranBands.BAND_21; - public static final int BAND_22 = EutranBands.BAND_22; - public static final int BAND_23 = EutranBands.BAND_23; - public static final int BAND_24 = EutranBands.BAND_24; - public static final int BAND_25 = EutranBands.BAND_25; - public static final int BAND_26 = EutranBands.BAND_26; - public static final int BAND_27 = EutranBands.BAND_27; - public static final int BAND_28 = EutranBands.BAND_28; - public static final int BAND_30 = EutranBands.BAND_30; - public static final int BAND_31 = EutranBands.BAND_31; - public static final int BAND_33 = EutranBands.BAND_33; - public static final int BAND_34 = EutranBands.BAND_34; - public static final int BAND_35 = EutranBands.BAND_35; - public static final int BAND_36 = EutranBands.BAND_36; - public static final int BAND_37 = EutranBands.BAND_37; - public static final int BAND_38 = EutranBands.BAND_38; - public static final int BAND_39 = EutranBands.BAND_39; - public static final int BAND_40 = EutranBands.BAND_40; - public static final int BAND_41 = EutranBands.BAND_41; - public static final int BAND_42 = EutranBands.BAND_42; - public static final int BAND_43 = EutranBands.BAND_43; - public static final int BAND_44 = EutranBands.BAND_44; - public static final int BAND_45 = EutranBands.BAND_45; - public static final int BAND_46 = EutranBands.BAND_46; - public static final int BAND_47 = EutranBands.BAND_47; - public static final int BAND_48 = EutranBands.BAND_48; - public static final int BAND_49 = EutranBands.BAND_49; - public static final int BAND_50 = EutranBands.BAND_50; - public static final int BAND_51 = EutranBands.BAND_51; - public static final int BAND_52 = EutranBands.BAND_52; - public static final int BAND_53 = EutranBands.BAND_53; - public static final int BAND_65 = EutranBands.BAND_65; - public static final int BAND_66 = EutranBands.BAND_66; - public static final int BAND_68 = EutranBands.BAND_68; - public static final int BAND_70 = EutranBands.BAND_70; - public static final int BAND_71 = EutranBands.BAND_71; - public static final int BAND_72 = EutranBands.BAND_72; - public static final int BAND_73 = EutranBands.BAND_73; - public static final int BAND_74 = EutranBands.BAND_74; - public static final int BAND_85 = EutranBands.BAND_85; - public static final int BAND_87 = EutranBands.BAND_87; - public static final int BAND_88 = EutranBands.BAND_88; + public static final int BAND_1 = android.hardware.radio.V1_5.EutranBands.BAND_1; + public static final int BAND_2 = android.hardware.radio.V1_5.EutranBands.BAND_2; + public static final int BAND_3 = android.hardware.radio.V1_5.EutranBands.BAND_3; + public static final int BAND_4 = android.hardware.radio.V1_5.EutranBands.BAND_4; + public static final int BAND_5 = android.hardware.radio.V1_5.EutranBands.BAND_5; + public static final int BAND_6 = android.hardware.radio.V1_5.EutranBands.BAND_6; + public static final int BAND_7 = android.hardware.radio.V1_5.EutranBands.BAND_7; + public static final int BAND_8 = android.hardware.radio.V1_5.EutranBands.BAND_8; + public static final int BAND_9 = android.hardware.radio.V1_5.EutranBands.BAND_9; + public static final int BAND_10 = android.hardware.radio.V1_5.EutranBands.BAND_10; + public static final int BAND_11 = android.hardware.radio.V1_5.EutranBands.BAND_11; + public static final int BAND_12 = android.hardware.radio.V1_5.EutranBands.BAND_12; + public static final int BAND_13 = android.hardware.radio.V1_5.EutranBands.BAND_13; + public static final int BAND_14 = android.hardware.radio.V1_5.EutranBands.BAND_14; + public static final int BAND_17 = android.hardware.radio.V1_5.EutranBands.BAND_17; + public static final int BAND_18 = android.hardware.radio.V1_5.EutranBands.BAND_18; + public static final int BAND_19 = android.hardware.radio.V1_5.EutranBands.BAND_19; + public static final int BAND_20 = android.hardware.radio.V1_5.EutranBands.BAND_20; + public static final int BAND_21 = android.hardware.radio.V1_5.EutranBands.BAND_21; + public static final int BAND_22 = android.hardware.radio.V1_5.EutranBands.BAND_22; + public static final int BAND_23 = android.hardware.radio.V1_5.EutranBands.BAND_23; + public static final int BAND_24 = android.hardware.radio.V1_5.EutranBands.BAND_24; + public static final int BAND_25 = android.hardware.radio.V1_5.EutranBands.BAND_25; + public static final int BAND_26 = android.hardware.radio.V1_5.EutranBands.BAND_26; + public static final int BAND_27 = android.hardware.radio.V1_5.EutranBands.BAND_27; + public static final int BAND_28 = android.hardware.radio.V1_5.EutranBands.BAND_28; + public static final int BAND_30 = android.hardware.radio.V1_5.EutranBands.BAND_30; + public static final int BAND_31 = android.hardware.radio.V1_5.EutranBands.BAND_31; + public static final int BAND_33 = android.hardware.radio.V1_5.EutranBands.BAND_33; + public static final int BAND_34 = android.hardware.radio.V1_5.EutranBands.BAND_34; + public static final int BAND_35 = android.hardware.radio.V1_5.EutranBands.BAND_35; + public static final int BAND_36 = android.hardware.radio.V1_5.EutranBands.BAND_36; + public static final int BAND_37 = android.hardware.radio.V1_5.EutranBands.BAND_37; + public static final int BAND_38 = android.hardware.radio.V1_5.EutranBands.BAND_38; + public static final int BAND_39 = android.hardware.radio.V1_5.EutranBands.BAND_39; + public static final int BAND_40 = android.hardware.radio.V1_5.EutranBands.BAND_40; + public static final int BAND_41 = android.hardware.radio.V1_5.EutranBands.BAND_41; + public static final int BAND_42 = android.hardware.radio.V1_5.EutranBands.BAND_42; + public static final int BAND_43 = android.hardware.radio.V1_5.EutranBands.BAND_43; + public static final int BAND_44 = android.hardware.radio.V1_5.EutranBands.BAND_44; + public static final int BAND_45 = android.hardware.radio.V1_5.EutranBands.BAND_45; + public static final int BAND_46 = android.hardware.radio.V1_5.EutranBands.BAND_46; + public static final int BAND_47 = android.hardware.radio.V1_5.EutranBands.BAND_47; + public static final int BAND_48 = android.hardware.radio.V1_5.EutranBands.BAND_48; + public static final int BAND_49 = android.hardware.radio.V1_5.EutranBands.BAND_49; + public static final int BAND_50 = android.hardware.radio.V1_5.EutranBands.BAND_50; + public static final int BAND_51 = android.hardware.radio.V1_5.EutranBands.BAND_51; + public static final int BAND_52 = android.hardware.radio.V1_5.EutranBands.BAND_52; + public static final int BAND_53 = android.hardware.radio.V1_5.EutranBands.BAND_53; + public static final int BAND_65 = android.hardware.radio.V1_5.EutranBands.BAND_65; + public static final int BAND_66 = android.hardware.radio.V1_5.EutranBands.BAND_66; + public static final int BAND_68 = android.hardware.radio.V1_5.EutranBands.BAND_68; + public static final int BAND_70 = android.hardware.radio.V1_5.EutranBands.BAND_70; + public static final int BAND_71 = android.hardware.radio.V1_5.EutranBands.BAND_71; + public static final int BAND_72 = android.hardware.radio.V1_5.EutranBands.BAND_72; + public static final int BAND_73 = android.hardware.radio.V1_5.EutranBands.BAND_73; + public static final int BAND_74 = android.hardware.radio.V1_5.EutranBands.BAND_74; + public static final int BAND_85 = android.hardware.radio.V1_5.EutranBands.BAND_85; + public static final int BAND_87 = android.hardware.radio.V1_5.EutranBands.BAND_87; + public static final int BAND_88 = android.hardware.radio.V1_5.EutranBands.BAND_88; + + /** + * EutranBands + * + * @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"BAND_"}, + value = {BAND_1, + BAND_2, + BAND_3, + BAND_4, + BAND_5, + BAND_6, + BAND_7, + BAND_8, + BAND_9, + BAND_10, + BAND_11, + BAND_12, + BAND_13, + BAND_14, + BAND_17, + BAND_18, + BAND_19, + BAND_20, + BAND_21, + BAND_22, + BAND_23, + BAND_24, + BAND_25, + BAND_26, + BAND_27, + BAND_28, + BAND_30, + BAND_31, + BAND_33, + BAND_34, + BAND_35, + BAND_36, + BAND_37, + BAND_38, + BAND_39, + BAND_40, + BAND_41, + BAND_42, + BAND_43, + BAND_44, + BAND_45, + BAND_46, + BAND_47, + BAND_48, + BAND_49, + BAND_50, + BAND_51, + BAND_52, + BAND_53, + BAND_65, + BAND_66, + BAND_68, + BAND_70, + BAND_71, + BAND_72, + BAND_73, + BAND_74, + BAND_85, + BAND_87, + BAND_88}) + + public @interface EutranBands {} /** @hide */ private EutranBand() {}; } /** + * 3GPP TS 36.101 Table 5.7.3-1 E-UTRA channel numbers. + * + * @hide + */ + enum EutranBandArfcnFrequency { + + EUTRAN_ARFCN_FREQUENCY_BAND_1( + EutranBand.BAND_1, 2110000, 0, 599, 1920000, 18800, 18599), + EUTRAN_ARFCN_FREQUENCY_BAND_2( + EutranBand.BAND_2, 1930000, 600, 1199, 1850000, 18600, 19199), + EUTRAN_ARFCN_FREQUENCY_BAND_3( + EutranBand.BAND_3, 1805000, 1200, 1949, 1710000, 19200, 19949), + EUTRAN_ARFCN_FREQUENCY_BAND_4( + EutranBand.BAND_4, 2110000, 1950, 2399, 1710000, 19950, 20399), + EUTRAN_ARFCN_FREQUENCY_BAND_5( + EutranBand.BAND_5, 869000, 2400, 2649, 824000, 20400, 20649), + EUTRAN_ARFCN_FREQUENCY_BAND_6( + EutranBand.BAND_6, 875000, 2650, 2749, 830000, 20650, 20749), + EUTRAN_ARFCN_FREQUENCY_BAND_7( + EutranBand.BAND_7, 2620000, 2750, 3449, 2500000, 20750, 21449), + EUTRAN_ARFCN_FREQUENCY_BAND_8( + EutranBand.BAND_8, 925000, 3450, 3799, 880000, 21450, 21799), + EUTRAN_ARFCN_FREQUENCY_BAND_9( + EutranBand.BAND_9, 1844900, 3800, 4149, 1749900, 21800, 22149), + EUTRAN_ARFCN_FREQUENCY_BAND_10( + EutranBand.BAND_10, 2110000, 4150, 4749, 1710000, 22150, 22749), + EUTRAN_ARFCN_FREQUENCY_BAND_11( + EutranBand.BAND_11, 1475900, 4750, 4949, 1427900, 22750, 22949), + EUTRAN_ARFCN_FREQUENCY_BAND_12( + EutranBand.BAND_12, 729000, 5010, 5179, 699000, 23010, 23179), + EUTRAN_ARFCN_FREQUENCY_BAND_13( + EutranBand.BAND_13, 746000, 5180, 5279, 777000, 23180, 23279), + EUTRAN_ARFCN_FREQUENCY_BAND_14( + EutranBand.BAND_14, 758000, 5280, 5379, 788000, 23230, 23379), + EUTRAN_ARFCN_FREQUENCY_BAND_17( + EutranBand.BAND_17, 734000, 5730, 5849, 704000, 23730, 23849), + EUTRAN_ARFCN_FREQUENCY_BAND_18( + EutranBand.BAND_18, 860000, 5850, 5999, 815000, 23850, 23999), + EUTRAN_ARFCN_FREQUENCY_BAND_19( + EutranBand.BAND_19, 875000, 6000, 6149, 830000, 24000, 24149), + EUTRAN_ARFCN_FREQUENCY_BAND_20( + EutranBand.BAND_20, 791000, 6150, 6449, 832000, 24150, 24449), + EUTRAN_ARFCN_FREQUENCY_BAND_21( + EutranBand.BAND_21, 1495900, 6450, 6599, 1447900, 24450, 24599), + EUTRAN_ARFCN_FREQUENCY_BAND_22( + EutranBand.BAND_22, 3510000, 6600, 7399, 3410000, 24600, 25399), + EUTRAN_ARFCN_FREQUENCY_BAND_23( + EutranBand.BAND_23, 2180000, 7500, 7699, 2000000, 25500, 25699), + EUTRAN_ARFCN_FREQUENCY_BAND_24( + EutranBand.BAND_24, 1525000, 7700, 8039, 1626500, 25700, 26039), + EUTRAN_ARFCN_FREQUENCY_BAND_25( + EutranBand.BAND_25, 1930000, 8040, 8689, 1850000, 26040, 26689), + EUTRAN_ARFCN_FREQUENCY_BAND_26( + EutranBand.BAND_26, 859000, 8690, 9039, 814000, 26690, 27039), + EUTRAN_ARFCN_FREQUENCY_BAND_27( + EutranBand.BAND_27, 852000, 9040, 9209, 807000, 27040, 27209), + EUTRAN_ARFCN_FREQUENCY_BAND_28( + EutranBand.BAND_28, 758000, 9210, 9659, 703000, 27210, 27659), + EUTRAN_ARFCN_FREQUENCY_BAND_30( + EutranBand.BAND_30, 2350000, 9770, 9869, 2305000, 27660, 27759), + EUTRAN_ARFCN_FREQUENCY_BAND_31( + EutranBand.BAND_31, 462500, 9870, 9919, 452500, 27760, 27809), + EUTRAN_ARFCN_FREQUENCY_BAND_33( + EutranBand.BAND_33, 1900000, 36000, 36199, 1900000, 36000, 36199), + EUTRAN_ARFCN_FREQUENCY_BAND_34( + EutranBand.BAND_34, 2010000, 36200, 36349, 2010000, 36200, 36349), + EUTRAN_ARFCN_FREQUENCY_BAND_35( + EutranBand.BAND_35, 1850000, 36350, 36949, 1850000, 36350, 36949), + EUTRAN_ARFCN_FREQUENCY_BAND_36( + EutranBand.BAND_36, 1930000, 36950, 37549, 1930000, 36950, 37549), + EUTRAN_ARFCN_FREQUENCY_BAND_37( + EutranBand.BAND_37, 1910000, 37550, 37749, 1910000, 37550, 37749), + EUTRAN_ARFCN_FREQUENCY_BAND_38( + EutranBand.BAND_38, 2570000, 37750, 38249, 2570000, 37750, 38249), + EUTRAN_ARFCN_FREQUENCY_BAND_39( + EutranBand.BAND_39, 1880000, 38250, 38649, 1880000, 38250, 38649), + EUTRAN_ARFCN_FREQUENCY_BAND_40( + EutranBand.BAND_40, 2300000, 38650, 39649, 2300000, 38650, 39649), + EUTRAN_ARFCN_FREQUENCY_BAND_41( + EutranBand.BAND_41, 2496000, 39650, 41589, 2496000, 39650, 41589), + EUTRAN_ARFCN_FREQUENCY_BAND_42( + EutranBand.BAND_42, 3400000, 41950, 43589, 3400000, 41950, 43589), + EUTRAN_ARFCN_FREQUENCY_BAND_43( + EutranBand.BAND_43, 3600000, 43950, 45589, 3600000, 43950, 45589), + EUTRAN_ARFCN_FREQUENCY_BAND_44( + EutranBand.BAND_44, 703000, 45590, 46589, 703000, 45590, 46589), + EUTRAN_ARFCN_FREQUENCY_BAND_45( + EutranBand.BAND_45, 1447000, 46590, 46789, 1447000, 46590, 46789), + EUTRAN_ARFCN_FREQUENCY_BAND_46( + EutranBand.BAND_46, 5150000, 46790, 54539, 5150000, 46790, 54539), + EUTRAN_ARFCN_FREQUENCY_BAND_47( + EutranBand.BAND_47, 5855000, 54540, 55239, 5855000, 54540, 55239), + EUTRAN_ARFCN_FREQUENCY_BAND_48( + EutranBand.BAND_48, 3550000, 55240, 56739, 3550000, 55240, 56739), + EUTRAN_ARFCN_FREQUENCY_BAND_49( + EutranBand.BAND_49, 3550000, 56740, 58239, 3550000, 56740, 58239), + EUTRAN_ARFCN_FREQUENCY_BAND_50( + EutranBand.BAND_50, 1432000, 58240, 59089, 1432000, 58240, 59089), + EUTRAN_ARFCN_FREQUENCY_BAND_51( + EutranBand.BAND_51, 1427000, 59090, 59139, 1427000, 59090, 59139), + EUTRAN_ARFCN_FREQUENCY_BAND_52( + EutranBand.BAND_52, 3300000, 59140, 60139, 3300000, 59140, 60139), + EUTRAN_ARFCN_FREQUENCY_BAND_53( + EutranBand.BAND_53, 2483500, 60140, 60254, 2483500, 60140, 60254), + EUTRAN_ARFCN_FREQUENCY_BAND_65( + EutranBand.BAND_65, 2110000, 65536, 66435, 1920000, 131072, 131971), + EUTRAN_ARFCN_FREQUENCY_BAND_66( + EutranBand.BAND_66, 2110000, 66436, 67335, 1710000, 131972, 132671), + EUTRAN_ARFCN_FREQUENCY_BAND_68( + EutranBand.BAND_68, 753000, 67536, 67835, 698000, 132672, 132971), + EUTRAN_ARFCN_FREQUENCY_BAND_70( + EutranBand.BAND_70, 1995000, 68336, 68585, 1695000, 132972, 133121), + EUTRAN_ARFCN_FREQUENCY_BAND_71( + EutranBand.BAND_71, 617000, 68586, 68935, 663000, 133122, 133471), + EUTRAN_ARFCN_FREQUENCY_BAND_72( + EutranBand.BAND_72, 461000, 68936, 68985, 451000, 133472, 133521), + EUTRAN_ARFCN_FREQUENCY_BAND_73( + EutranBand.BAND_73, 460000, 68986, 69035, 450000, 133522, 133571), + EUTRAN_ARFCN_FREQUENCY_BAND_74( + EutranBand.BAND_74, 1475000, 69036, 69465, 1427000, 133572, 134001), + EUTRAN_ARFCN_FREQUENCY_BAND_85( + EutranBand.BAND_85, 728000, 70366, 70545, 698000, 134002, 134181), + EUTRAN_ARFCN_FREQUENCY_BAND_87( + EutranBand.BAND_87, 420000, 70546, 70595, 410000, 134182, 134231), + EUTRAN_ARFCN_FREQUENCY_BAND_88( + EutranBand.BAND_88, 422000, 70596, 70645, 412000, 134231, 134280); + + EutranBandArfcnFrequency(int band, int downlinkLowKhz, int downlinkOffset, + int downlinkRange, int uplinkLowKhz, int uplinkOffset, + int uplinkRange) { + this.band = band; + this.downlinkLowKhz = downlinkLowKhz; + this.downlinkOffset = downlinkOffset; + this.uplinkLowKhz = uplinkLowKhz; + this.uplinkOffset = uplinkOffset; + this.downlinkRange = downlinkRange; + this.uplinkRange = uplinkRange; + } + + int band; + int downlinkLowKhz; + int downlinkOffset; + int uplinkLowKhz; + int uplinkOffset; + int downlinkRange; + int uplinkRange; + } + + /** * Frequency bands for CDMA2000. * http://www.3gpp2.org/Public_html/Specs/C.S0057-E_v1.0_Bandclass_Specification.pdf * @hide @@ -320,7 +693,7 @@ public final class AccessNetworkConstants { * https://www.etsi.org/deliver/etsi_ts/138100_138199/13810102/15.08.00_60/ts_13810102v150800p.pdf */ public static final class NgranBands { - /** 3GPP TS 38.101-1, Version 16.2.0, Table 5.2-1: FR1 bands */ + /** 3GPP TS 38.101-1, Version 16.5.0, Table 5.2-1: FR1 bands */ public static final int BAND_1 = android.hardware.radio.V1_5.NgranBands.BAND_1; public static final int BAND_2 = android.hardware.radio.V1_5.NgranBands.BAND_2; public static final int BAND_3 = android.hardware.radio.V1_5.NgranBands.BAND_3; @@ -332,6 +705,7 @@ public final class AccessNetworkConstants { public static final int BAND_18 = android.hardware.radio.V1_5.NgranBands.BAND_18; public static final int BAND_20 = android.hardware.radio.V1_5.NgranBands.BAND_20; public static final int BAND_25 = android.hardware.radio.V1_5.NgranBands.BAND_25; + public static final int BAND_26 = android.hardware.radio.V1_6.NgranBands.BAND_26; public static final int BAND_28 = android.hardware.radio.V1_5.NgranBands.BAND_28; public static final int BAND_29 = android.hardware.radio.V1_5.NgranBands.BAND_29; public static final int BAND_30 = android.hardware.radio.V1_5.NgranBands.BAND_30; @@ -340,9 +714,11 @@ public final class AccessNetworkConstants { public static final int BAND_39 = android.hardware.radio.V1_5.NgranBands.BAND_39; public static final int BAND_40 = android.hardware.radio.V1_5.NgranBands.BAND_40; public static final int BAND_41 = android.hardware.radio.V1_5.NgranBands.BAND_41; + public static final int BAND_46 = android.hardware.radio.V1_6.NgranBands.BAND_46; public static final int BAND_48 = android.hardware.radio.V1_5.NgranBands.BAND_48; public static final int BAND_50 = android.hardware.radio.V1_5.NgranBands.BAND_50; public static final int BAND_51 = android.hardware.radio.V1_5.NgranBands.BAND_51; + public static final int BAND_53 = android.hardware.radio.V1_6.NgranBands.BAND_53; public static final int BAND_65 = android.hardware.radio.V1_5.NgranBands.BAND_65; public static final int BAND_66 = android.hardware.radio.V1_5.NgranBands.BAND_66; public static final int BAND_70 = android.hardware.radio.V1_5.NgranBands.BAND_70; @@ -366,6 +742,7 @@ public final class AccessNetworkConstants { public static final int BAND_93 = android.hardware.radio.V1_5.NgranBands.BAND_93; public static final int BAND_94 = android.hardware.radio.V1_5.NgranBands.BAND_94; public static final int BAND_95 = android.hardware.radio.V1_5.NgranBands.BAND_95; + public static final int BAND_96 = android.hardware.radio.V1_6.NgranBands.BAND_96; /** 3GPP TS 38.101-2, Version 16.2.0, Table 5.2-1: FR2 bands */ public static final int BAND_257 = android.hardware.radio.V1_5.NgranBands.BAND_257; @@ -390,6 +767,7 @@ public final class AccessNetworkConstants { BAND_18, BAND_20, BAND_25, + BAND_26, BAND_28, BAND_29, BAND_30, @@ -398,9 +776,11 @@ public final class AccessNetworkConstants { BAND_39, BAND_40, BAND_41, + BAND_46, BAND_48, BAND_50, BAND_51, + BAND_53, BAND_65, BAND_66, BAND_70, @@ -424,6 +804,7 @@ public final class AccessNetworkConstants { BAND_93, BAND_94, BAND_95, + BAND_96, BAND_257, BAND_258, BAND_260, @@ -464,7 +845,8 @@ public final class AccessNetworkConstants { value = { FREQUENCY_RANGE_GROUP_UNKNOWN, FREQUENCY_RANGE_GROUP_1, - FREQUENCY_RANGE_GROUP_2}) + FREQUENCY_RANGE_GROUP_2 + }) public @interface FrequencyRangeGroup {} /** @@ -489,6 +871,7 @@ public final class AccessNetworkConstants { case BAND_18: case BAND_20: case BAND_25: + case BAND_26: case BAND_28: case BAND_29: case BAND_30: @@ -497,9 +880,11 @@ public final class AccessNetworkConstants { case BAND_39: case BAND_40: case BAND_41: + case BAND_46: case BAND_48: case BAND_50: case BAND_51: + case BAND_53: case BAND_65: case BAND_66: case BAND_70: @@ -523,6 +908,7 @@ public final class AccessNetworkConstants { case BAND_93: case BAND_94: case BAND_95: + case BAND_96: return FREQUENCY_RANGE_GROUP_1; case BAND_257: case BAND_258: @@ -538,6 +924,33 @@ public final class AccessNetworkConstants { private NgranBands() {} } + /** + * 3GPP TS 38.104 Table 5.4.2.1-1 NR-ARFCN parameters for the global frequency raster. + * + * @hide + */ + enum NgranArfcnFrequency { + + NGRAN_ARFCN_FREQUENCY_RANGE_1(5, 0, 0, 0, 599999), + NGRAN_ARFCN_FREQUENCY_RANGE_2(15, 3000000, 600000, 600000, 2016666), + NGRAN_ARFCN_FREQUENCY_RANGE_3(60, 24250080, 2016667, 2016667, 3279165); + + NgranArfcnFrequency(int globalKhz, int rangeOffset, int arfcnOffset, + int rangeFirst, int rangeLast) { + this.globalKhz = globalKhz; + this.rangeOffset = rangeOffset; + this.arfcnOffset = arfcnOffset; + this.rangeFirst = rangeFirst; + this.rangeLast = rangeLast; + } + + int globalKhz; + int rangeOffset; + int arfcnOffset; + int rangeFirst; + int rangeLast; + } + /** @hide */ private AccessNetworkConstants() {}; } diff --git a/telephony/java/android/telephony/AccessNetworkUtils.java b/telephony/java/android/telephony/AccessNetworkUtils.java index 7661a32f6d5b..f29f3bd352be 100644 --- a/telephony/java/android/telephony/AccessNetworkUtils.java +++ b/telephony/java/android/telephony/AccessNetworkUtils.java @@ -4,12 +4,20 @@ import static android.telephony.ServiceState.DUPLEX_MODE_FDD; import static android.telephony.ServiceState.DUPLEX_MODE_TDD; import static android.telephony.ServiceState.DUPLEX_MODE_UNKNOWN; +import android.telephony.AccessNetworkConstants.EutranBandArfcnFrequency; import android.telephony.AccessNetworkConstants.EutranBand; import android.telephony.AccessNetworkConstants.GeranBand; +import android.telephony.AccessNetworkConstants.GeranBandArfcnFrequency; +import android.telephony.AccessNetworkConstants.NgranArfcnFrequency; +import android.telephony.AccessNetworkConstants.NgranBands; import android.telephony.AccessNetworkConstants.UtranBand; +import android.telephony.AccessNetworkConstants.UtranBandArfcnFrequency; import android.telephony.ServiceState.DuplexMode; +import android.util.Log; import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; /** * Utilities to map between radio constants. @@ -22,9 +30,27 @@ public class AccessNetworkUtils { private AccessNetworkUtils() {} public static final int INVALID_BAND = -1; + public static final int INVALID_FREQUENCY = -1; /** ISO country code of Japan. */ private static final String JAPAN_ISO_COUNTRY_CODE = "jp"; + private static final String TAG = "AccessNetworkUtils"; + + private static final int FREQUENCY_KHZ = 1000; + private static final int FREQUENCY_RANGE_LOW_KHZ = 1000000; + private static final int FREQUENCY_RANGE_MID_KHZ = 3000000; + private static final int FREQUENCY_RANGE_HIGH_KHZ = 6000000; + + private static final Set<Integer> UARFCN_NOT_GENERAL_BAND; + static { + UARFCN_NOT_GENERAL_BAND = new HashSet<Integer>(); + UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_A); + UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_B); + UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_C); + UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_D); + UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_E); + UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_F); + } /** * Gets the duplex mode for the given EUTRAN operating band. @@ -325,4 +351,403 @@ public class AccessNetworkUtils { } return INVALID_BAND; } + + /** + * Get geran bands from {@link PhysicalChannelConfig#getBand()} + */ + public static int getFrequencyRangeGroupFromGeranBand(@GeranBand.GeranBands int band) { + switch (band) { + case GeranBand.BAND_T380: + case GeranBand.BAND_T410: + case GeranBand.BAND_450: + case GeranBand.BAND_480: + case GeranBand.BAND_710: + case GeranBand.BAND_750: + case GeranBand.BAND_T810: + case GeranBand.BAND_850: + case GeranBand.BAND_P900: + case GeranBand.BAND_E900: + case GeranBand.BAND_R900: + case GeranBand.BAND_ER900: + return ServiceState.FREQUENCY_RANGE_LOW; + case GeranBand.BAND_DCS1800: + case GeranBand.BAND_PCS1900: + return ServiceState.FREQUENCY_RANGE_MID; + default: + return ServiceState.FREQUENCY_RANGE_UNKNOWN; + } + } + + /** + * Get utran bands from {@link PhysicalChannelConfig#getBand()} + */ + public static int getFrequencyRangeGroupFromUtranBand(@UtranBand.UtranBands int band) { + switch (band) { + case UtranBand.BAND_5: + case UtranBand.BAND_6: + case UtranBand.BAND_8: + case UtranBand.BAND_12: + case UtranBand.BAND_13: + case UtranBand.BAND_14: + case UtranBand.BAND_19: + case UtranBand.BAND_20: + case UtranBand.BAND_26: + return ServiceState.FREQUENCY_RANGE_LOW; + case UtranBand.BAND_1: + case UtranBand.BAND_2: + case UtranBand.BAND_3: + case UtranBand.BAND_4: + case UtranBand.BAND_7: + case UtranBand.BAND_9: + case UtranBand.BAND_10: + case UtranBand.BAND_11: + case UtranBand.BAND_21: + case UtranBand.BAND_25: + case UtranBand.BAND_A: + case UtranBand.BAND_B: + case UtranBand.BAND_C: + case UtranBand.BAND_D: + case UtranBand.BAND_E: + case UtranBand.BAND_F: + return ServiceState.FREQUENCY_RANGE_MID; + case UtranBand.BAND_22: + return ServiceState.FREQUENCY_RANGE_HIGH; + default: + return ServiceState.FREQUENCY_RANGE_UNKNOWN; + } + } + + /** + * Get eutran bands from {@link PhysicalChannelConfig#getBand()} + * 3GPP TS 36.101 Table 5.5 EUTRA operating bands + */ + public static int getFrequencyRangeGroupFromEutranBand(@EutranBand.EutranBands int band) { + switch (band) { + case EutranBand.BAND_5: + case EutranBand.BAND_6: + case EutranBand.BAND_8: + case EutranBand.BAND_12: + case EutranBand.BAND_13: + case EutranBand.BAND_14: + case EutranBand.BAND_17: + case EutranBand.BAND_18: + case EutranBand.BAND_19: + case EutranBand.BAND_20: + case EutranBand.BAND_26: + case EutranBand.BAND_27: + case EutranBand.BAND_28: + case EutranBand.BAND_31: + case EutranBand.BAND_44: + case EutranBand.BAND_50: + case EutranBand.BAND_51: + case EutranBand.BAND_68: + case EutranBand.BAND_71: + case EutranBand.BAND_72: + case EutranBand.BAND_73: + case EutranBand.BAND_85: + case EutranBand.BAND_87: + case EutranBand.BAND_88: + return ServiceState.FREQUENCY_RANGE_LOW; + case EutranBand.BAND_1: + case EutranBand.BAND_2: + case EutranBand.BAND_3: + case EutranBand.BAND_4: + case EutranBand.BAND_7: + case EutranBand.BAND_9: + case EutranBand.BAND_10: + case EutranBand.BAND_11: + case EutranBand.BAND_21: + case EutranBand.BAND_23: + case EutranBand.BAND_24: + case EutranBand.BAND_25: + case EutranBand.BAND_30: + case EutranBand.BAND_33: + case EutranBand.BAND_34: + case EutranBand.BAND_35: + case EutranBand.BAND_36: + case EutranBand.BAND_37: + case EutranBand.BAND_38: + case EutranBand.BAND_39: + case EutranBand.BAND_40: + case EutranBand.BAND_41: + case EutranBand.BAND_45: + case EutranBand.BAND_53: + case EutranBand.BAND_65: + case EutranBand.BAND_66: + case EutranBand.BAND_70: + case EutranBand.BAND_74: + return ServiceState.FREQUENCY_RANGE_MID; + case EutranBand.BAND_22: + case EutranBand.BAND_42: + case EutranBand.BAND_43: + case EutranBand.BAND_46: + case EutranBand.BAND_47: + case EutranBand.BAND_48: + case EutranBand.BAND_49: + case EutranBand.BAND_52: + return ServiceState.FREQUENCY_RANGE_HIGH; + default: + return ServiceState.FREQUENCY_RANGE_UNKNOWN; + } + } + + /** + * Get ngran band from {@link PhysicalChannelConfig#getBand()} + * 3GPP TS 38.104 Table 5.2-1 NR operating bands in FR1 + * 3GPP TS 38.104 Table 5.2-2 NR operating bands in FR2 + */ + public static int getFrequencyRangeGroupFromNrBand(@NgranBands.NgranBand int band) { + switch (band) { + case NgranBands.BAND_5: + case NgranBands.BAND_8: + case NgranBands.BAND_12: + case NgranBands.BAND_14: + case NgranBands.BAND_18: + case NgranBands.BAND_20: + case NgranBands.BAND_26: + case NgranBands.BAND_28: + case NgranBands.BAND_29: + case NgranBands.BAND_71: + case NgranBands.BAND_81: + case NgranBands.BAND_82: + case NgranBands.BAND_83: + case NgranBands.BAND_89: + return ServiceState.FREQUENCY_RANGE_LOW; + case NgranBands.BAND_1: + case NgranBands.BAND_2: + case NgranBands.BAND_3: + case NgranBands.BAND_7: + case NgranBands.BAND_25: + case NgranBands.BAND_30: + case NgranBands.BAND_34: + case NgranBands.BAND_38: + case NgranBands.BAND_39: + case NgranBands.BAND_40: + case NgranBands.BAND_41: + case NgranBands.BAND_50: + case NgranBands.BAND_51: + case NgranBands.BAND_53: + case NgranBands.BAND_65: + case NgranBands.BAND_66: + case NgranBands.BAND_70: + case NgranBands.BAND_74: + case NgranBands.BAND_75: + case NgranBands.BAND_76: + case NgranBands.BAND_80: + case NgranBands.BAND_84: + case NgranBands.BAND_86: + case NgranBands.BAND_90: + case NgranBands.BAND_91: + case NgranBands.BAND_92: + case NgranBands.BAND_93: + case NgranBands.BAND_94: + case NgranBands.BAND_95: + return ServiceState.FREQUENCY_RANGE_MID; + case NgranBands.BAND_46: + case NgranBands.BAND_48: + case NgranBands.BAND_77: + case NgranBands.BAND_78: + case NgranBands.BAND_79: + return ServiceState.FREQUENCY_RANGE_HIGH; + case NgranBands.BAND_96: + case NgranBands.BAND_257: + case NgranBands.BAND_258: + case NgranBands.BAND_260: + case NgranBands.BAND_261: + return ServiceState.FREQUENCY_RANGE_MMWAVE; + default: + return ServiceState.FREQUENCY_RANGE_UNKNOWN; + } + } + + /** + * 3GPP TS 38.104 Table 5.4.2.1-1 NR-ARFCN parameters for the global frequency raster. + * Formula of NR-ARFCN convert to actual frequency: + * Actual frequency(kHz) = (RANGE_OFFSET + GLOBAL_KHZ * (ARFCN - ARFCN_OFFSET)) + */ + public static int getFrequencyFromNrArfcn(int nrArfcn) { + + int globalKhz = 0; + int rangeOffset = 0; + int arfcnOffset = 0; + for (NgranArfcnFrequency nrArfcnFrequency : AccessNetworkConstants. + NgranArfcnFrequency.values()) { + if (nrArfcn >= nrArfcnFrequency.rangeFirst + && nrArfcn <= nrArfcnFrequency.rangeLast) { + globalKhz = nrArfcnFrequency.globalKhz; + rangeOffset = nrArfcnFrequency.rangeOffset; + arfcnOffset = nrArfcnFrequency.arfcnOffset; + break; + } + } + return rangeOffset + globalKhz * (nrArfcn - arfcnOffset); + } + + /** + * Get actual frequency from E-UTRA ARFCN. + */ + public static int getFrequencyFromEarfcn(int band, int earfcn, boolean isUplink) { + + int low = 0; + int offset = 0; + for (EutranBandArfcnFrequency earfcnFrequency : EutranBandArfcnFrequency.values()) { + if (band == earfcnFrequency.band) { + if (isInEarfcnRange(earfcn, earfcnFrequency, isUplink)) { + low = isUplink ? earfcnFrequency.uplinkLowKhz : earfcnFrequency.downlinkLowKhz; + offset = isUplink ? earfcnFrequency.uplinkOffset + : earfcnFrequency.downlinkOffset; + break; + } else { + Log.e(TAG, "Band and the range of EARFCN are not consistent."); + return INVALID_FREQUENCY; + } + } + } + return convertEarfcnToFrequency(low, earfcn, offset); + } + + /** + * 3GPP TS 36.101 Table 5.7.3-1 E-UTRA channel numbers. + * Formula of E-UTRA ARFCN convert to actual frequency: + * Actual frequency(kHz) = (DOWNLINK_LOW + 0.1 * (ARFCN - DOWNLINK_OFFSET)) * FREQUENCY_KHZ + * Actual frequency(kHz) = (UPLINK_LOW + 0.1 * (ARFCN - UPLINK_OFFSET)) * FREQUENCY_KHZ + */ + private static int convertEarfcnToFrequency(int low, int earfcn, int offset) { + return low + 100 * (earfcn - offset); + } + + private static boolean isInEarfcnRange(int earfcn, EutranBandArfcnFrequency earfcnFrequency, + boolean isUplink) { + if (isUplink) { + return earfcn >= earfcnFrequency.uplinkOffset && earfcn <= earfcnFrequency.uplinkRange; + } else { + return earfcn >= earfcnFrequency.downlinkOffset + && earfcn <= earfcnFrequency.downlinkRange; + } + } + + /** + * Get actual frequency from UTRA ARFCN. + */ + public static int getFrequencyFromUarfcn(int band, int uarfcn, boolean isUplink) { + + int offsetKhz = 0; + for (UtranBandArfcnFrequency uarfcnFrequency : AccessNetworkConstants. + UtranBandArfcnFrequency.values()) { + if (band == uarfcnFrequency.band) { + if (isInUarfcnRange(uarfcn, uarfcnFrequency, isUplink)) { + offsetKhz = isUplink ? uarfcnFrequency.uplinkOffset + : uarfcnFrequency.downlinkOffset; + break; + } else { + Log.e(TAG, "Band and the range of UARFCN are not consistent."); + return INVALID_FREQUENCY; + } + } + } + + if (!UARFCN_NOT_GENERAL_BAND.contains(band)) { + return convertUarfcnToFrequency(offsetKhz, uarfcn); + } else { + return convertUarfcnTddToFrequency(band, uarfcn); + } + } + + /** + * 3GPP TS 25.101, Table 5.1 UARFCN definition (general). + * Formula of UTRA ARFCN convert to actual frequency: + * For general bands: + * Downlink actual frequency(kHz) = (DOWNLINK_OFFSET + 0.2 * ARFCN) * FREQUENCY_KHZ + * Uplink actual frequency(kHz) = (UPLINK_OFFSET + 0.2 * ARFCN) * FREQUENCY_KHZ + */ + private static int convertUarfcnToFrequency(int offsetKhz, int uarfcn) { + return offsetKhz + (200 * uarfcn); + } + + /** + * 3GPP TS 25.102, Table 5.2 UTRA Absolute Radio Frequency Channel Number 1.28 Mcps TDD Option. + * For FDD bands A, B, C, E, F: + * Actual frequency(kHz) = 5 * ARFCN * FREQUENCY_KHZ + * For TDD bands D: + * Actual frequency(kHz) = (5 * (ARFCN - 2150.1MHz)) * FREQUENCY_KHZ + */ + private static int convertUarfcnTddToFrequency(int band, int uarfcn) { + if (band != UtranBand.BAND_D) { + return 5 * uarfcn * FREQUENCY_KHZ; + } else { + return 5 * ((FREQUENCY_KHZ * uarfcn) - 2150100); + } + } + + private static boolean isInUarfcnRange(int uarfcn, UtranBandArfcnFrequency uarfcnFrequency, + boolean isUplink) { + if (isUplink) { + return uarfcn >= uarfcnFrequency.uplinkRangeFirst + && uarfcn <= uarfcnFrequency.uplinkRangeLast; + } else { + if (uarfcnFrequency.downlinkRangeFirst != 0 && uarfcnFrequency.downlinkRangeLast != 0) { + return uarfcn >= uarfcnFrequency.downlinkRangeFirst + && uarfcn <= uarfcnFrequency.downlinkRangeLast; + } else { + // BAND_C, BAND_D, BAND_E and BAND_F do not have the downlink range. + return true; + } + } + } + + /** + * Get actual frequency from GERAN ARFCN. + */ + public static int getFrequencyFromArfcn(int band, int arfcn, boolean isUplink) { + + int uplinkFrequencyFirst = 0; + int arfcnOffset = 0; + int downlinkOffset = 0; + int frequency = 0; + for (GeranBandArfcnFrequency arfcnFrequency : AccessNetworkConstants. + GeranBandArfcnFrequency.values()) { + if (band == arfcnFrequency.band) { + if (arfcn >= arfcnFrequency.arfcnRangeFirst + && arfcn <= arfcnFrequency.arfcnRangeLast) { + uplinkFrequencyFirst = arfcnFrequency.uplinkFrequencyFirst; + downlinkOffset = arfcnFrequency.downlinkOffset; + arfcnOffset = arfcnFrequency.arfcnOffset; + frequency = convertArfcnToFrequency(arfcn, uplinkFrequencyFirst, + arfcnOffset); + break; + } else { + Log.e(TAG, "Band and the range of ARFCN are not consistent."); + return INVALID_FREQUENCY; + } + } + } + + return isUplink ? frequency : frequency + downlinkOffset; + } + + /** + * 3GPP TS 45.005 Table 2-1 Dynamically mapped ARFCN + * Formula of Geran ARFCN convert to actual frequency: + * Uplink actual frequency(kHz) = + * (UPLINK_FREQUENCY_FIRST + 0.2 * (ARFCN - ARFCN_RANGE_FIRST)) * FREQUENCY_KHZ + * Downlink actual frequency(kHz) = Uplink actual frequency + 10 + */ + private static int convertArfcnToFrequency(int arfcn, int uplinkFrequencyFirstKhz, + int arfcnOffset) { + return uplinkFrequencyFirstKhz + 200 * (arfcn - arfcnOffset); + } + + public static int getFrequencyRangeFromArfcn(int frequency) { + if (frequency < FREQUENCY_RANGE_LOW_KHZ) { + return ServiceState.FREQUENCY_RANGE_LOW; + } else if (frequency < FREQUENCY_RANGE_MID_KHZ + && frequency >= FREQUENCY_RANGE_LOW_KHZ) { + return ServiceState.FREQUENCY_RANGE_MID; + } else if (frequency < FREQUENCY_RANGE_HIGH_KHZ + && frequency >= FREQUENCY_RANGE_MID_KHZ) { + return ServiceState.FREQUENCY_RANGE_HIGH; + } else { + return ServiceState.FREQUENCY_RANGE_MMWAVE; + } + } } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 94fb812e2c3b..daa3d1f0c509 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -4643,6 +4643,13 @@ public class CarrierConfigManager { */ public static final String KEY_USE_ACS_FOR_RCS_BOOL = "use_acs_for_rcs_bool"; + /** + * Indicates temporarily unmetered mobile data is supported by the carrier. + * @hide + */ + public static final String KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL = + "network_temp_not_metered_supported_bool"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -5193,6 +5200,7 @@ public class CarrierConfigManager { sDefaults.putString(KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING, ""); sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false); sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false); + sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, false); } /** @@ -5211,9 +5219,25 @@ public class CarrierConfigManager { public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT = KEY_PREFIX + "hotspot_maximum_client_count"; + /** + * This configuration is intended to be a narrow exception for provisioning + * {@link android.net.wifi.WifiNetworkSuggestion} of widely-known carrier networks that do + * not support using randomized MAC address. + * Carrier provisioned {@link android.net.wifi.WifiNetworkSuggestion} with SSIDs included + * in this list will have MAC randomization disabled. + * + * Note: the SSIDs in the list are expected to be interpreted as is - do not add double + * quotes to the SSIDs. + */ + public static final String KEY_SUGGESTION_SSID_LIST_WITH_MAC_RANDOMIZATION_DISABLED = + KEY_PREFIX + "suggestion_ssid_list_with_mac_randomization_disabled"; + private static PersistableBundle getDefaults() { PersistableBundle defaults = new PersistableBundle(); defaults.putInt(KEY_HOTSPOT_MAX_CLIENT_COUNT, 0); + defaults.putStringArray(KEY_SUGGESTION_SSID_LIST_WITH_MAC_RANDOMIZATION_DISABLED, + new String[0]); + return defaults; } diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java index 95c69ba470c4..b359ebed2683 100644 --- a/telephony/java/android/telephony/PhysicalChannelConfig.java +++ b/telephony/java/android/telephony/PhysicalChannelConfig.java @@ -19,7 +19,6 @@ package android.telephony; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; -import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.telephony.Annotation.NetworkType; @@ -56,6 +55,18 @@ public final class PhysicalChannelConfig implements Parcelable { /** Physical Cell Id is unknown. */ public static final int PHYSICAL_CELL_ID_UNKNOWN = -1; + /** Physical Cell Id's maximum value is 1007. */ + public static final int PHYSICAL_CELL_ID_MAXIMUM_VALUE = 1007; + + /** Cell bandwidth is unknown. */ + public static final int CELL_BANDWIDTH_UNKNOWN = 0; + + /** The frequency is unknown. */ + public static final int FREQUENCY_UNKNOWN = -1; + + /** The band is unknown. */ + public static final int BAND_UNKNOWN = 0; + /** * Connection status of the cell. * @@ -65,15 +76,20 @@ public final class PhysicalChannelConfig implements Parcelable { private int mCellConnectionStatus; /** - * Cell bandwidth, in kHz. + * Downlink cell bandwidth, in kHz. */ private int mCellBandwidthDownlinkKhz; /** + * Uplink cell bandwidth, in kHz. + */ + private int mCellBandwidthUplinkKhz; + + /** * The radio technology for this physical channel. */ @NetworkType - private int mRat; + private int mNetworkType; /** * The rough frequency range for this physical channel. @@ -82,9 +98,24 @@ public final class PhysicalChannelConfig implements Parcelable { private int mFrequencyRange; /** - * The absolute radio frequency channel number, {@link #CHANNEL_NUMBER_UNKNOWN} if unknown. + * The frequency of Downlink. + */ + private int mDownlinkFrequency; + + /** + * The frequency of Uplink. + */ + private int mUplinkFrequency; + + /** + * Downlink Absolute Radio Frequency Channel Number + */ + private int mDownlinkChannelNumber; + + /** + * Uplink Absolute Radio Frequency Channel Number */ - private int mChannelNumber; + private int mUplinkChannelNumber; /** * A list of data calls mapped to this physical channel. An empty list means the physical @@ -98,6 +129,11 @@ public final class PhysicalChannelConfig implements Parcelable { */ private int mPhysicalCellId; + /** + * This is the band which is being used. + */ + private int mBand; + @Override public int describeContents() { return 0; @@ -107,27 +143,39 @@ public final class PhysicalChannelConfig implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mCellConnectionStatus); dest.writeInt(mCellBandwidthDownlinkKhz); - dest.writeInt(mRat); - dest.writeInt(mChannelNumber); + dest.writeInt(mCellBandwidthUplinkKhz); + dest.writeInt(mNetworkType); + dest.writeInt(mDownlinkChannelNumber); + dest.writeInt(mUplinkChannelNumber); dest.writeInt(mFrequencyRange); dest.writeIntArray(mContextIds); dest.writeInt(mPhysicalCellId); + dest.writeInt(mBand); } /** - * @return Cell bandwidth, in kHz + * @return Downlink cell bandwidth in kHz, {@link #CELL_BANDWIDTH_UNKNOWN} if unknown. */ - public int getCellBandwidthDownlink() { + @IntRange(from = 1) + public int getCellBandwidthDownlinkKhz() { return mCellBandwidthDownlinkKhz; } /** + * @return Uplink cell bandwidth in kHz, {@link #CELL_BANDWIDTH_UNKNOWN} if unknown. + */ + @IntRange(from = 1) + public int getCellBandwidthUplinkKhz() { + return mCellBandwidthUplinkKhz; + } + + /** * Get the list of data call ids mapped to this physical channel. This list is sorted into * ascending numerical order. Each id in this list must match the id in * {@link com.android.internal.telephony.dataconnection.DataConnection}. An empty list means the * physical channel has no data call mapped to it. * - * @return an integer list indicates the data call ids. + * @return an integer list indicates the data call ids, * @hide */ public int[] getContextIds() { @@ -135,7 +183,18 @@ public final class PhysicalChannelConfig implements Parcelable { } /** - * @return the rough frequency range for this physical channel. + * @return the absolute radio frequency channel number for this physical channel, + * {@link #CHANNEL_NUMBER_UNKNOWN} if unknown. + * @deprecated Use {@link #getDownlinkChannelNumber()} to get the channel number. + */ + @Deprecated + public int getChannelNumber() { + return getDownlinkChannelNumber(); + } + + /** + * @return the rough frequency range for this physical channel, + * {@link ServiceState#FREQUENCY_RANGE_UNKNOWN} if unknown. * @see {@link ServiceState#FREQUENCY_RANGE_LOW} * @see {@link ServiceState#FREQUENCY_RANGE_MID} * @see {@link ServiceState#FREQUENCY_RANGE_HIGH} @@ -148,11 +207,48 @@ public final class PhysicalChannelConfig implements Parcelable { } /** - * @return the absolute radio frequency channel number for this physical channel, + * @return Downlink Absolute Radio Frequency Channel Number, * {@link #CHANNEL_NUMBER_UNKNOWN} if unknown. */ - public int getChannelNumber() { - return mChannelNumber; + @IntRange(from = 0) + public int getDownlinkChannelNumber() { + return mDownlinkChannelNumber; + } + + /** + * @return Uplink Absolute Radio Frequency Channel Number, + * {@link #CHANNEL_NUMBER_UNKNOWN} if unknown. + */ + @IntRange(from = 0) + public int getUplinkChannelNumber() { + return mUplinkChannelNumber; + } + + /** + * The valid bands are {@link AccessNetworkConstants.GeranBand}, + * {@link AccessNetworkConstants.UtranBand}, {@link AccessNetworkConstants.EutranBand} and + * {@link AccessNetworkConstants.NgranBands}. + * + * @return the frequency band, {@link #BAND_UNKNOWN} if unknown. */ + @IntRange(from = 1, to = 261) + public int getBand() { + return mBand; + } + + /** + * @return The downlink frequency in kHz, {@link #FREQUENCY_UNKNOWN} if unknown. + */ + @IntRange(from = 0) + public int getDownlinkFrequencyKhz() { + return mDownlinkFrequency; + } + + /** + * @return The uplink frequency in kHz, {@link #FREQUENCY_UNKNOWN} if unknown. + */ + @IntRange(from = 0) + public int getUplinkFrequencyKhz() { + return mUplinkFrequency; } /** @@ -173,10 +269,13 @@ public final class PhysicalChannelConfig implements Parcelable { return mPhysicalCellId; } - /**The radio technology for this physical channel. */ + /** + * @return The network type for this physical channel, + * {@link TelephonyManager#NETWORK_TYPE_UNKNOWN} if unknown. + */ @NetworkType public int getNetworkType() { - return mRat; + return mNetworkType; } /** @@ -186,7 +285,7 @@ public final class PhysicalChannelConfig implements Parcelable { * @see #CONNECTION_SECONDARY_SERVING * @see #CONNECTION_UNKNOWN * - * @return Connection status of the cell + * @return Connection status of the cell, {@link #CONNECTION_UNKNOWN} if unknown. */ @ConnectionStatus public int getConnectionStatus() { @@ -210,6 +309,97 @@ public final class PhysicalChannelConfig implements Parcelable { } } + private void setDownlinkFrequency() { + switch (mNetworkType) { + case TelephonyManager.NETWORK_TYPE_NR: + mDownlinkFrequency = AccessNetworkUtils.getFrequencyFromNrArfcn( + mDownlinkChannelNumber); + break; + case TelephonyManager.NETWORK_TYPE_LTE: + mDownlinkFrequency = AccessNetworkUtils.getFrequencyFromEarfcn( + mBand, mDownlinkChannelNumber, false); + break; + case TelephonyManager.NETWORK_TYPE_HSPAP: + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: + case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + mDownlinkFrequency = AccessNetworkUtils.getFrequencyFromUarfcn( + mBand, mDownlinkChannelNumber, false); + break; + case TelephonyManager.NETWORK_TYPE_GPRS: + case TelephonyManager.NETWORK_TYPE_EDGE: + case TelephonyManager.NETWORK_TYPE_GSM: + mDownlinkFrequency = AccessNetworkUtils.getFrequencyFromArfcn( + mBand, mDownlinkChannelNumber, false); + break; + } + } + + private void setUplinkFrequency() { + switch (mNetworkType){ + case TelephonyManager.NETWORK_TYPE_NR: + mUplinkFrequency = mDownlinkFrequency; + break; + case TelephonyManager.NETWORK_TYPE_LTE: + mUplinkFrequency = AccessNetworkUtils.getFrequencyFromEarfcn( + mBand, mUplinkChannelNumber, true); + break; + case TelephonyManager.NETWORK_TYPE_HSPAP: + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: + case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + mUplinkFrequency = AccessNetworkUtils.getFrequencyFromUarfcn( + mBand, mUplinkChannelNumber, true); + break; + case TelephonyManager.NETWORK_TYPE_GPRS: + case TelephonyManager.NETWORK_TYPE_EDGE: + case TelephonyManager.NETWORK_TYPE_GSM: + mUplinkFrequency = AccessNetworkUtils.getFrequencyFromArfcn( + mBand, mUplinkChannelNumber, true); + break; + } + } + + private void setFrequencyRange() { + if (mFrequencyRange != ServiceState.FREQUENCY_RANGE_UNKNOWN) { + return; + } + + switch (mNetworkType) { + case TelephonyManager.NETWORK_TYPE_NR: + mFrequencyRange = AccessNetworkUtils.getFrequencyRangeGroupFromNrBand(mBand); + break; + case TelephonyManager.NETWORK_TYPE_LTE: + mFrequencyRange = AccessNetworkUtils.getFrequencyRangeGroupFromEutranBand(mBand); + break; + case TelephonyManager.NETWORK_TYPE_HSPAP: + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: + case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + mFrequencyRange = AccessNetworkUtils.getFrequencyRangeGroupFromUtranBand(mBand); + break; + case TelephonyManager.NETWORK_TYPE_GPRS: + case TelephonyManager.NETWORK_TYPE_EDGE: + case TelephonyManager.NETWORK_TYPE_GSM: + mFrequencyRange = AccessNetworkUtils.getFrequencyRangeGroupFromGeranBand(mBand); + break; + default: + mFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN; + break; + } + + if (mFrequencyRange == ServiceState.FREQUENCY_RANGE_UNKNOWN) { + mFrequencyRange = AccessNetworkUtils.getFrequencyRangeFromArfcn( + mDownlinkFrequency); + } + } + @Override public boolean equals(Object o) { if (this == o) { @@ -223,30 +413,37 @@ public final class PhysicalChannelConfig implements Parcelable { PhysicalChannelConfig config = (PhysicalChannelConfig) o; return mCellConnectionStatus == config.mCellConnectionStatus && mCellBandwidthDownlinkKhz == config.mCellBandwidthDownlinkKhz - && mRat == config.mRat + && mCellBandwidthUplinkKhz == config.mCellBandwidthUplinkKhz + && mNetworkType == config.mNetworkType && mFrequencyRange == config.mFrequencyRange - && mChannelNumber == config.mChannelNumber + && mDownlinkChannelNumber == config.mDownlinkChannelNumber + && mUplinkChannelNumber == config.mUplinkChannelNumber && mPhysicalCellId == config.mPhysicalCellId - && Arrays.equals(mContextIds, config.mContextIds); + && Arrays.equals(mContextIds, config.mContextIds) + && mBand == config.mBand + && mDownlinkFrequency == config.mDownlinkFrequency + && mUplinkFrequency == config.mUplinkFrequency; } @Override public int hashCode() { return Objects.hash( - mCellConnectionStatus, mCellBandwidthDownlinkKhz, mRat, mFrequencyRange, - mChannelNumber, mPhysicalCellId, mContextIds); + mCellConnectionStatus, mCellBandwidthDownlinkKhz, mCellBandwidthUplinkKhz, + mNetworkType, mFrequencyRange, mDownlinkChannelNumber, mUplinkChannelNumber, + mContextIds, mPhysicalCellId, mBand, mDownlinkFrequency, mUplinkFrequency); } - public static final @android.annotation.NonNull Parcelable.Creator<PhysicalChannelConfig> CREATOR = - new Parcelable.Creator<PhysicalChannelConfig>() { - public PhysicalChannelConfig createFromParcel(Parcel in) { - return new PhysicalChannelConfig(in); - } + public static final + @android.annotation.NonNull Parcelable.Creator<PhysicalChannelConfig> CREATOR = + new Parcelable.Creator<PhysicalChannelConfig>() { + public PhysicalChannelConfig createFromParcel(Parcel in) { + return new PhysicalChannelConfig(in); + } - public PhysicalChannelConfig[] newArray(int size) { - return new PhysicalChannelConfig[size]; - } - }; + public PhysicalChannelConfig[] newArray(int size) { + return new PhysicalChannelConfig[size]; + } + }; @Override public String toString() { @@ -255,44 +452,64 @@ public final class PhysicalChannelConfig implements Parcelable { .append(getConnectionStatusString()) .append(",mCellBandwidthDownlinkKhz=") .append(mCellBandwidthDownlinkKhz) - .append(",mRat=") - .append(TelephonyManager.getNetworkTypeName(mRat)) + .append(",mCellBandwidthUplinkKhz=") + .append(mCellBandwidthUplinkKhz) + .append(",mNetworkType=") + .append(TelephonyManager.getNetworkTypeName(mNetworkType)) .append(",mFrequencyRange=") .append(ServiceState.frequencyRangeToString(mFrequencyRange)) - .append(",mChannelNumber=") - .append(mChannelNumber) + .append(",mDownlinkChannelNumber=") + .append(mDownlinkChannelNumber) + .append(",mUplinkChannelNumber=") + .append(mUplinkChannelNumber) .append(",mContextIds=") .append(Arrays.toString(mContextIds)) .append(",mPhysicalCellId=") .append(mPhysicalCellId) + .append(",mBand=") + .append(mBand) + .append(",mDownlinkFrequency=") + .append(mDownlinkFrequency) + .append(",mUplinkFrequency=") + .append(mUplinkFrequency) .append("}") .toString(); } - /** @hide */ - public PhysicalChannelConfig(int status, int bandwidth) { - mCellConnectionStatus = status; - mCellBandwidthDownlinkKhz = bandwidth; - } - private PhysicalChannelConfig(Parcel in) { mCellConnectionStatus = in.readInt(); mCellBandwidthDownlinkKhz = in.readInt(); - mRat = in.readInt(); - mChannelNumber = in.readInt(); + mCellBandwidthUplinkKhz = in.readInt(); + mNetworkType = in.readInt(); + mDownlinkChannelNumber = in.readInt(); + mUplinkChannelNumber = in.readInt(); mFrequencyRange = in.readInt(); mContextIds = in.createIntArray(); mPhysicalCellId = in.readInt(); + mBand = in.readInt(); + if (mBand > BAND_UNKNOWN) { + setDownlinkFrequency(); + setUplinkFrequency(); + setFrequencyRange(); + } } private PhysicalChannelConfig(Builder builder) { mCellConnectionStatus = builder.mCellConnectionStatus; mCellBandwidthDownlinkKhz = builder.mCellBandwidthDownlinkKhz; - mRat = builder.mRat; - mChannelNumber = builder.mChannelNumber; + mCellBandwidthUplinkKhz = builder.mCellBandwidthUplinkKhz; + mNetworkType = builder.mNetworkType; + mDownlinkChannelNumber = builder.mDownlinkChannelNumber; + mUplinkChannelNumber = builder.mUplinkChannelNumber; mFrequencyRange = builder.mFrequencyRange; mContextIds = builder.mContextIds; mPhysicalCellId = builder.mPhysicalCellId; + mBand = builder.mBand; + if (mBand > BAND_UNKNOWN) { + setDownlinkFrequency(); + setUplinkFrequency(); + setFrequencyRange(); + } } /** @@ -300,61 +517,105 @@ public final class PhysicalChannelConfig implements Parcelable { * @hide */ public static final class Builder { - private int mRat; + private int mNetworkType; private int mFrequencyRange; - private int mChannelNumber; + private int mDownlinkChannelNumber; + private int mUplinkChannelNumber; private int mCellBandwidthDownlinkKhz; + private int mCellBandwidthUplinkKhz; private int mCellConnectionStatus; private int[] mContextIds; private int mPhysicalCellId; + private int mBand; public Builder() { - mRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN; + mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; mFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN; - mChannelNumber = CHANNEL_NUMBER_UNKNOWN; - mCellBandwidthDownlinkKhz = 0; + mDownlinkChannelNumber = CHANNEL_NUMBER_UNKNOWN; + mUplinkChannelNumber = CHANNEL_NUMBER_UNKNOWN; + mCellBandwidthDownlinkKhz = CELL_BANDWIDTH_UNKNOWN; + mCellBandwidthUplinkKhz = CELL_BANDWIDTH_UNKNOWN; mCellConnectionStatus = CONNECTION_UNKNOWN; mContextIds = new int[0]; mPhysicalCellId = PHYSICAL_CELL_ID_UNKNOWN; + mBand = BAND_UNKNOWN; } public PhysicalChannelConfig build() { return new PhysicalChannelConfig(this); } - public Builder setRat(int rat) { - this.mRat = rat; + public @NonNull Builder setNetworkType(@NetworkType int networkType) { + if (!TelephonyManager.isNetworkTypeValid(networkType)) { + throw new IllegalArgumentException("Network type: " + networkType + " is invalid."); + } + mNetworkType = networkType; return this; } - public Builder setFrequencyRange(int frequencyRange) { - this.mFrequencyRange = frequencyRange; + public @NonNull Builder setFrequencyRange(int frequencyRange) { + if (!ServiceState.isFrequencyRangeValid(frequencyRange)) { + throw new IllegalArgumentException("Frequency range: " + frequencyRange + + " is invalid."); + } + mFrequencyRange = frequencyRange; return this; } - public Builder setChannelNumber(int channelNumber) { - this.mChannelNumber = channelNumber; + public @NonNull Builder setDownlinkChannelNumber(int downlinkChannelNumber) { + mDownlinkChannelNumber = downlinkChannelNumber; return this; } - public Builder setCellBandwidthDownlinkKhz(int cellBandwidthDownlinkKhz) { - this.mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz; + public @NonNull Builder setUplinkChannelNumber(int uplinkChannelNumber) { + mUplinkChannelNumber = uplinkChannelNumber; return this; } - public Builder setCellConnectionStatus(int connectionStatus) { - this.mCellConnectionStatus = connectionStatus; + public @NonNull Builder setCellBandwidthDownlinkKhz(int cellBandwidthDownlinkKhz) { + if (cellBandwidthDownlinkKhz < CELL_BANDWIDTH_UNKNOWN) { + throw new IllegalArgumentException("Cell downlink bandwidth(kHz): " + + cellBandwidthDownlinkKhz + " is invalid."); + } + mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz; return this; } - public Builder setContextIds(int[] contextIds) { + public @NonNull Builder setCellBandwidthUplinkKhz(int cellBandwidthUplinkKhz) { + if (cellBandwidthUplinkKhz < CELL_BANDWIDTH_UNKNOWN) { + throw new IllegalArgumentException("Cell uplink bandwidth(kHz): "+ + cellBandwidthUplinkKhz +" is invalid."); + } + mCellBandwidthUplinkKhz = cellBandwidthUplinkKhz; + return this; + } + + public @NonNull Builder setCellConnectionStatus(int connectionStatus) { + mCellConnectionStatus = connectionStatus; + return this; + } + + public @NonNull Builder setContextIds(int[] contextIds) { if (contextIds != null) Arrays.sort(contextIds); - this.mContextIds = contextIds; + mContextIds = contextIds; return this; } - public Builder setPhysicalCellId(int physicalCellId) { - this.mPhysicalCellId = physicalCellId; + public @NonNull Builder setPhysicalCellId(int physicalCellId) { + if (physicalCellId > PHYSICAL_CELL_ID_MAXIMUM_VALUE) { + throw new IllegalArgumentException("Physical cell Id: " + physicalCellId + + " is over limit."); + } + mPhysicalCellId = physicalCellId; + return this; + } + + public @NonNull Builder setBand(int band) { + if (band <= BAND_UNKNOWN) { + throw new IllegalArgumentException("Band: " + band + + " is invalid."); + } + mBand = band; return this; } } diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index dedb1afa2495..f110daecd952 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -2111,4 +2111,23 @@ public class ServiceState implements Parcelable { } return false; } + + /** + * The frequency range is valid or not. + * + * @param frequencyRange The frequency range {@link FrequencyRange}. + * @return {@code true} if valid, {@code false} otherwise. + * + * @hide + */ + public static boolean isFrequencyRangeValid(int frequencyRange) { + if (frequencyRange == FREQUENCY_RANGE_LOW + || frequencyRange == FREQUENCY_RANGE_MID + || frequencyRange == FREQUENCY_RANGE_HIGH + || frequencyRange == FREQUENCY_RANGE_MMWAVE) { + return true; + } else { + return false; + } + } } diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java new file mode 100644 index 000000000000..2a1640234144 --- /dev/null +++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +/** + * Request used to register {@link SignalThresholdInfo} to be notified when the signal strength + * breach the specified thresholds. + */ +public final class SignalStrengthUpdateRequest implements Parcelable { + /** + * List of SignalThresholdInfo for the request. + */ + private final List<SignalThresholdInfo> mSignalThresholdInfos; + + /** + * Whether the reporting is required for thresholds in the request while device is idle. + */ + private final boolean mIsReportingRequestedWhileIdle; + + /** + * Whether the reporting requested for system thresholds while device is idle. + * + * System signal thresholds are loaded from carrier config items and mainly used for UI + * displaying. By default, they are ignored when device is idle. When setting the value to true, + * modem will continue reporting signal strength changes over the system signal thresholds even + * device is idle. + * + * This should only set to true by the system caller. + */ + private final boolean mIsSystemThresholdReportingRequestedWhileIdle; + + private SignalStrengthUpdateRequest( + @NonNull List<SignalThresholdInfo> signalThresholdInfos, + boolean isReportingRequestedWhileIdle, + boolean isSystemThresholdReportingRequestedWhileIdle) { + validate(signalThresholdInfos); + + mSignalThresholdInfos = signalThresholdInfos; + mIsReportingRequestedWhileIdle = isReportingRequestedWhileIdle; + mIsSystemThresholdReportingRequestedWhileIdle = + isSystemThresholdReportingRequestedWhileIdle; + } + + /** + * Builder class to create {@link SignalStrengthUpdateRequest} object. + */ + public static final class Builder { + private List<SignalThresholdInfo> mSignalThresholdInfos = null; + private boolean mIsReportingRequestedWhileIdle = false; + private boolean mIsSystemThresholdReportingRequestedWhileIdle = false; + + /** + * Set the collection of SignalThresholdInfo for the builder object + * + * @param signalThresholdInfos the collection of SignalThresholdInfo + * + * @return the builder to facilitate the chaining + */ + public @NonNull Builder setSignalThresholdInfos( + @NonNull Collection<SignalThresholdInfo> signalThresholdInfos) { + Objects.requireNonNull(signalThresholdInfos, + "SignalThresholdInfo collection must not be null"); + for (SignalThresholdInfo info : signalThresholdInfos) { + Objects.requireNonNull(info, + "SignalThresholdInfo in the collection must not be null"); + } + + mSignalThresholdInfos = new ArrayList<>(signalThresholdInfos); + // Sort the collection with RAN ascending order, make the ordering not matter for equals + mSignalThresholdInfos.sort( + Comparator.comparingInt(SignalThresholdInfo::getRadioAccessNetworkType)); + return this; + } + + /** + * Set the builder object if require reporting on thresholds in this request when device is + * idle. + * + * @param isReportingRequestedWhileIdle true if request reporting when device is idle + * + * @return the builder to facilitate the chaining + */ + public @NonNull Builder setReportingRequestedWhileIdle( + boolean isReportingRequestedWhileIdle) { + mIsReportingRequestedWhileIdle = isReportingRequestedWhileIdle; + return this; + } + + /** + * Set the builder object if require reporting on the system thresholds when device is idle. + * + * This can only used by the system caller. + * + * @param isSystemThresholdReportingRequestedWhileIdle true if request reporting on the + * system thresholds when device is idle + * @return the builder to facilitate the chaining + * @hide + */ + public @NonNull Builder setSystemThresholdReportingRequestedWhileIdle( + boolean isSystemThresholdReportingRequestedWhileIdle) { + mIsSystemThresholdReportingRequestedWhileIdle = + isSystemThresholdReportingRequestedWhileIdle; + return this; + } + + /** + * Build a {@link SignalStrengthUpdateRequest} object. + * + * @return the SignalStrengthUpdateRequest object + * + * @throws IllegalArgumentException if the SignalThresholdInfo collection is empty size, the + * radio access network type in the collection is not unique + */ + public @NonNull SignalStrengthUpdateRequest build() { + return new SignalStrengthUpdateRequest(mSignalThresholdInfos, + mIsReportingRequestedWhileIdle, mIsSystemThresholdReportingRequestedWhileIdle); + } + } + + private SignalStrengthUpdateRequest(Parcel in) { + mSignalThresholdInfos = in.createTypedArrayList(SignalThresholdInfo.CREATOR); + mIsReportingRequestedWhileIdle = in.readBoolean(); + mIsSystemThresholdReportingRequestedWhileIdle = in.readBoolean(); + } + + /** + * Get the collection of SignalThresholdInfo in the request. + * + * @return the collection of SignalThresholdInfo + */ + @NonNull + public Collection<SignalThresholdInfo> getSignalThresholdInfos() { + return Collections.unmodifiableList(mSignalThresholdInfos); + } + + /** + * Get whether reporting is requested for the threshold in the request while device is idle. + * + * @return true if reporting requested while device is idle + */ + public boolean isReportingRequestedWhileIdle() { + return mIsReportingRequestedWhileIdle; + } + + /** + * @return true if reporting requested for system thresholds while device is idle + * + * @hide + */ + public boolean isSystemThresholdReportingRequestedWhileIdle() { + return mIsSystemThresholdReportingRequestedWhileIdle; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedList(mSignalThresholdInfos); + dest.writeBoolean(mIsReportingRequestedWhileIdle); + dest.writeBoolean(mIsSystemThresholdReportingRequestedWhileIdle); + } + + @Override + public boolean equals(Object other) { + if (this == other) return true; + + if (!(other instanceof SignalStrengthUpdateRequest)) { + return false; + } + + SignalStrengthUpdateRequest request = (SignalStrengthUpdateRequest) other; + return request.mSignalThresholdInfos.equals(mSignalThresholdInfos) + && request.mIsReportingRequestedWhileIdle == mIsReportingRequestedWhileIdle + && request.mIsSystemThresholdReportingRequestedWhileIdle + == mIsSystemThresholdReportingRequestedWhileIdle; + } + + @Override + public int hashCode() { + return Objects.hash(mSignalThresholdInfos, mIsReportingRequestedWhileIdle, + mIsSystemThresholdReportingRequestedWhileIdle); + } + + public static final @NonNull Parcelable.Creator<SignalStrengthUpdateRequest> CREATOR = + new Parcelable.Creator<SignalStrengthUpdateRequest>() { + @Override + public SignalStrengthUpdateRequest createFromParcel(Parcel source) { + return new SignalStrengthUpdateRequest(source); + } + + @Override + public SignalStrengthUpdateRequest[] newArray(int size) { + return new SignalStrengthUpdateRequest[size]; + } + }; + + @Override + public String toString() { + return new StringBuilder("SignalStrengthUpdateRequest{") + .append("mSignalThresholdInfos=") + .append(mSignalThresholdInfos) + .append(" mIsReportingRequestedWhileIdle=") + .append(mIsReportingRequestedWhileIdle) + .append(" mIsSystemThresholdReportingRequestedWhileIdle=") + .append(mIsSystemThresholdReportingRequestedWhileIdle) + .append("}").toString(); + } + + /** + * Throw IAE when the RAN in the collection is not unique. + */ + private static void validate(Collection<SignalThresholdInfo> infos) { + Set<Integer> uniqueRan = new HashSet<>(infos.size()); + for (SignalThresholdInfo info : infos) { + final int ran = info.getRadioAccessNetworkType(); + if (!uniqueRan.add(ran)) { + throw new IllegalArgumentException("RAN: " + ran + " is not unique"); + } + } + } +} diff --git a/telephony/java/android/telephony/SignalThresholdInfo.java b/telephony/java/android/telephony/SignalThresholdInfo.java index 8a472adb9e03..0059ad6c2426 100644 --- a/telephony/java/android/telephony/SignalThresholdInfo.java +++ b/telephony/java/android/telephony/SignalThresholdInfo.java @@ -28,13 +28,11 @@ import java.util.Objects; /** * Defines the threshold value of the signal strength. - * @hide */ public final class SignalThresholdInfo implements Parcelable { /** * Unknown signal measurement type. - * @hide */ public static final int SIGNAL_MEASUREMENT_TYPE_UNKNOWN = 0; @@ -44,7 +42,6 @@ public final class SignalThresholdInfo implements Parcelable { * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#GERAN}, * {@link AccessNetworkConstants.AccessNetworkType#CDMA2000} * Reference: 3GPP TS 27.007 section 8.5. - * @hide */ public static final int SIGNAL_MEASUREMENT_TYPE_RSSI = 1; @@ -53,7 +50,6 @@ public final class SignalThresholdInfo implements Parcelable { * Range: -120 dBm to -25 dBm; * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#UTRAN} * Reference: 3GPP TS 25.123, section 9.1.1.1 - * @hide */ public static final int SIGNAL_MEASUREMENT_TYPE_RSCP = 2; @@ -62,7 +58,6 @@ public final class SignalThresholdInfo implements Parcelable { * Range: -140 dBm to -44 dBm; * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN} * Reference: 3GPP TS 36.133 9.1.4 - * @hide */ public static final int SIGNAL_MEASUREMENT_TYPE_RSRP = 3; @@ -71,7 +66,6 @@ public final class SignalThresholdInfo implements Parcelable { * Range: -34 dB to 3 dB; * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN} * Reference: 3GPP TS 36.133 9.1.7 - * @hide */ public static final int SIGNAL_MEASUREMENT_TYPE_RSRQ = 4; @@ -79,7 +73,6 @@ public final class SignalThresholdInfo implements Parcelable { * Reference Signal Signal to Noise Ratio * Range: -20 dB to 30 dB; * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN} - * @hide */ public static final int SIGNAL_MEASUREMENT_TYPE_RSSNR = 5; @@ -88,7 +81,6 @@ public final class SignalThresholdInfo implements Parcelable { * Range: -140 dBm to -44 dBm. * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN} * Reference: 3GPP TS 38.215. - * @hide */ public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRP = 6; @@ -97,7 +89,6 @@ public final class SignalThresholdInfo implements Parcelable { * Range: -43 dB to 20 dB. * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN} * Reference: 3GPP TS 38.133 section 10.1.11.1. - * @hide */ public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRQ = 7; @@ -106,7 +97,6 @@ public final class SignalThresholdInfo implements Parcelable { * Range: -23 dB to 40 dB * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN} * Reference: 3GPP TS 38.215 section 5.1.*, 3GPP TS 38.133 section 10.1.16.1. - * @hide */ public static final int SIGNAL_MEASUREMENT_TYPE_SSSINR = 8; @@ -290,6 +280,20 @@ public final class SignalThresholdInfo implements Parcelable { public static final int SIGNAL_SSSINR_MAX_VALUE = 40; /** + * The minimum number of thresholds allowed in each SignalThresholdInfo. + * + * @hide + */ + public static final int MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED = 1; + + /** + * The maximum number of thresholds allowed in each SignalThresholdInfo. + * + * @hide + */ + public static final int MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED = 4; + + /** * Constructor * * @param ran Radio Access Network type @@ -316,8 +320,6 @@ public final class SignalThresholdInfo implements Parcelable { /** * Builder class to create {@link SignalThresholdInfo} objects. - * - * @hide */ public static final class Builder { private int mRan = AccessNetworkConstants.AccessNetworkType.UNKNOWN; @@ -378,21 +380,58 @@ public final class SignalThresholdInfo implements Parcelable { } /** - * Set the signal threshold values of the corresponding signal measurement type. + * Set the signal strength thresholds of the corresponding signal measurement type. * - * The range and unit must reference specific SignalMeasurementType. + * The range and unit must reference specific SignalMeasurementType. The length of the + * thresholds should between the numbers return from + * {@link #getMinimumNumberOfThresholdsAllowed()} and + * {@link #getMaximumNumberOfThresholdsAllowed()}. An IllegalArgumentException will throw + * otherwise. * * @param thresholds array of integer as the signal threshold values * @return the builder to facilitate the chaining + * + * @see #SIGNAL_MEASUREMENT_TYPE_RSSI + * @see #SIGNAL_MEASUREMENT_TYPE_RSCP + * @see #SIGNAL_MEASUREMENT_TYPE_RSRP + * @see #SIGNAL_MEASUREMENT_TYPE_RSRQ + * @see #SIGNAL_MEASUREMENT_TYPE_RSSNR + * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRP + * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRQ + * @see #SIGNAL_MEASUREMENT_TYPE_SSSINR + * @see #getThresholds() for more details on signal strength thresholds */ public @NonNull Builder setThresholds(@NonNull int[] thresholds) { Objects.requireNonNull(thresholds, "thresholds must not be null"); + if (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED + || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED) { + throw new IllegalArgumentException( + "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED + + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED); + } mThresholds = thresholds.clone(); Arrays.sort(mThresholds); return this; } /** + * Set the signal strength thresholds for the corresponding signal measurement type without + * the length limitation. + * + * @param thresholds array of integer as the signal threshold values + * @return the builder to facilitate the chaining + * + * @hide + */ + public @NonNull Builder setThresholdsUnlimited(@NonNull int[] thresholds) { + Objects.requireNonNull(thresholds, "thresholds must not be null"); + mThresholds = thresholds.clone(); + Arrays.sort(mThresholds); + return this; + } + + + /** * Set if the modem should trigger the report based on the criteria. * * @param isEnabled true if the modem should trigger the report based on the criteria @@ -423,8 +462,6 @@ public final class SignalThresholdInfo implements Parcelable { * Get the radio access network type. * * @return radio access network type - * - * @hide */ public @AccessNetworkConstants.RadioAccessNetworkType int getRadioAccessNetworkType() { return mRan; @@ -434,8 +471,6 @@ public final class SignalThresholdInfo implements Parcelable { * Get the signal measurement type. * * @return the SignalMeasurementType value - * - * @hide */ public @SignalMeasurementType int getSignalMeasurementType() { return mSignalMeasurementType; @@ -457,16 +492,47 @@ public final class SignalThresholdInfo implements Parcelable { } /** - * Get the signal threshold values. + * Get the signal strength thresholds. + * + * Signal strength thresholds are a list of integer used for suggesting signal level and signal + * reporting criteria. The range and unit must reference specific SignalMeasurementType. + * + * Please refer to https://source.android.com/devices/tech/connect/signal-strength on how signal + * strength thresholds are used for signal strength reporting. * * @return array of integer of the signal thresholds * - * @hide + * @see #SIGNAL_MEASUREMENT_TYPE_RSSI + * @see #SIGNAL_MEASUREMENT_TYPE_RSCP + * @see #SIGNAL_MEASUREMENT_TYPE_RSRP + * @see #SIGNAL_MEASUREMENT_TYPE_RSRQ + * @see #SIGNAL_MEASUREMENT_TYPE_RSSNR + * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRP + * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRQ + * @see #SIGNAL_MEASUREMENT_TYPE_SSSINR */ public @NonNull int[] getThresholds() { return mThresholds.clone(); } + /** + * Get the minimum number of thresholds allowed in each SignalThresholdInfo. + * + * @return the minimum number of thresholds allowed + */ + public static int getMinimumNumberOfThresholdsAllowed() { + return MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED; + } + + /** + * Get the maximum number of threshold allowed in each SignalThresholdInfo. + * + * @return the maximum number of thresholds allowed + */ + public static int getMaximumNumberOfThresholdsAllowed() { + return MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED; + } + @Override public int describeContents() { return 0; diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 2190eb130d62..2f577a99028e 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -131,10 +131,12 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.UUID; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -15185,4 +15187,17 @@ public class TelephonyManager { e.execute(() -> callback.onAuthenticationFailure(GBA_FAILURE_REASON_FEATURE_NOT_READY)); } } + + /** + * The network type is valid or not. + * + * @param networkType The network type {@link NetworkType}. + * @return {@code true} if valid, {@code false} otherwise. + * + * @hide + */ + public static boolean isNetworkTypeValid(@NetworkType int networkType) { + return networkType >= TelephonyManager.NETWORK_TYPE_UNKNOWN && + networkType <= TelephonyManager.NETWORK_TYPE_NR; + } } diff --git a/telephony/java/android/telephony/euicc/DownloadableSubscription.java b/telephony/java/android/telephony/euicc/DownloadableSubscription.java index 34120790b25a..a5150b010f57 100644 --- a/telephony/java/android/telephony/euicc/DownloadableSubscription.java +++ b/telephony/java/android/telephony/euicc/DownloadableSubscription.java @@ -121,25 +121,21 @@ public final class DownloadableSubscription implements Parcelable { } public Builder(@NonNull String encodedActivationCode) { - Preconditions.checkNotNull(encodedActivationCode, "Activation code may not be null"); this.encodedActivationCode = encodedActivationCode; } /** - * Builds a {@link DownloadableSubscription} object. If the encoded activation code is - * {@code null}, a {@link NullPointerException} will be thrown. + * Builds a {@link DownloadableSubscription} object. * @return a non-null {@link DownloadableSubscription} object. */ @NonNull public DownloadableSubscription build() { - Preconditions.checkNotNull(encodedActivationCode, "Activation code may not be null"); return new DownloadableSubscription(encodedActivationCode, confirmationCode, carrierName, accessRules); } /** - * Sets the encoded activation code. If the encoded activation code is {@code null}, a - * {@link NullPointerException} will be thrown. + * Sets the encoded activation code. * @param value the activation code to use. An activation code can be parsed from a user * scanned QR code. The format of activation code is defined in SGP.22. For * example, "1$SMDP.GSMA.COM$04386-AGYFT-A74Y8-3F815$1.3.6.1.4.1.31746". For @@ -147,7 +143,6 @@ public final class DownloadableSubscription implements Parcelable { */ @NonNull public Builder setEncodedActivationCode(@NonNull String value) { - Preconditions.checkNotNull(encodedActivationCode, "Activation code may not be null"); encodedActivationCode = value; return this; } diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index 9a2def935f5d..1a940c75cfa4 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -1016,6 +1016,15 @@ </intent-filter> </activity> + <activity android:name="RippleActivity" + android:label="Animation/Ripple Animation" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="com.android.test.hwui.TEST"/> + </intent-filter> + </activity> + <activity android:name="MultiProducerActivity" android:label="Threads/Multiple Producers" android:exported="true"> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java index b53b78a8d5b2..8be3b7e35d42 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java @@ -35,9 +35,6 @@ import android.graphics.Shader; import android.os.Bundle; import android.view.View; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - @SuppressWarnings({"UnusedDeclaration"}) public class ColorFiltersMutateActivity extends Activity { @Override @@ -54,11 +51,13 @@ public class ColorFiltersMutateActivity extends Activity { private final Paint mLightingPaint; private final Paint mBlendPaint; private final Paint mShaderPaint; + private final RuntimeShader mRuntimeShader; private float mSaturation = 0.0f; private int mLightAdd = 0; private int mLightMul = 0; private int mPorterDuffColor = 0; + private float mShaderParam1 = 0.0f; static final String sSkSL = "in shader bitmapShader;\n" @@ -67,8 +66,6 @@ public class ColorFiltersMutateActivity extends Activity { + " return half4(sample(bitmapShader, xy).rgb, param1);\n" + "}\n"; - private byte[] mUniforms = new byte[4]; - BitmapsView(Context c) { super(c); @@ -86,11 +83,13 @@ public class ColorFiltersMutateActivity extends Activity { mBlendPaint = new Paint(); mBlendPaint.setColorFilter(new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_OVER)); + mRuntimeShader = new RuntimeShader(sSkSL, false); + mRuntimeShader.setUniform("param1", mShaderParam1); + mRuntimeShader.setInputShader("bitmapShader", new BitmapShader(mBitmap1, + Shader.TileMode.CLAMP, + Shader.TileMode.CLAMP)); mShaderPaint = new Paint(); - Shader[] inputShaders = { new BitmapShader(mBitmap1, Shader.TileMode.CLAMP, - Shader.TileMode.CLAMP) }; - mShaderPaint.setShader(new RuntimeShader(sSkSL, mUniforms, inputShaders, true)); - setShaderParam1(0.0f); + mShaderPaint.setShader(mRuntimeShader); ObjectAnimator sat = ObjectAnimator.ofFloat(this, "saturation", 1.0f); sat.setDuration(1000); @@ -177,20 +176,15 @@ public class ColorFiltersMutateActivity extends Activity { } public void setShaderParam1(float value) { - RuntimeShader shader = (RuntimeShader) mShaderPaint.getShader(); - ByteBuffer buffer = ByteBuffer.wrap(mUniforms); - buffer.order(ByteOrder.LITTLE_ENDIAN); - buffer.putFloat(value); - shader.updateUniforms(mUniforms); + mShaderParam1 = value; + mRuntimeShader.setUniform("param1", mShaderParam1); invalidate(); } // If either valueFrom or valueTo is null, then a getter function will also be derived // and called by the animator class. public float getShaderParam1() { - ByteBuffer buffer = ByteBuffer.wrap(mUniforms); - buffer.order(ByteOrder.LITTLE_ENDIAN); - return buffer.getFloat(); + return mShaderParam1; } @Override diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java new file mode 100644 index 000000000000..f6d9a7301949 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.hwui; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.CanvasProperty; +import android.graphics.Paint; +import android.graphics.RecordingCanvas; +import android.graphics.RuntimeShader; +import android.os.Bundle; +import android.os.Trace; +import android.view.RenderNodeAnimator; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.LinearLayout.LayoutParams; + +import java.util.ArrayList; + +public class RippleActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + layout.addView(new RippleView(this), + new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + + setContentView(layout); + } + + static class RippleView extends View { + static final int DURATION = 1000; + static final int MAX_RADIUS = 250; + + private boolean mToggle = false; + ArrayList<RenderNodeAnimator> mRunningAnimations = new ArrayList<RenderNodeAnimator>(); + + CanvasProperty<Float> mX; + CanvasProperty<Float> mY; + CanvasProperty<Float> mRadius; + CanvasProperty<Float> mProgress; + CanvasProperty<Paint> mPaint; + RuntimeShader mRuntimeShader; + + static final String sSkSL = "" + + "uniform float2 in_origin;" + + "uniform float in_progress;\n" + + "uniform float in_maxRadius;\n" + + "uniform shader in_paintColor;\n" + + "float dist2(float2 p0, float2 pf) { return sqrt((pf.x - p0.x) * (pf.x - p0.x) + " + + "(pf.y - p0.y) * (pf.y - p0.y)); }\n" + + "float mod2(float a, float b) { return a - (b * floor(a / b)); }\n" + + "float rand(float2 src) { return fract(sin(dot(src.xy, float2(12.9898, 78.233)))" + + " * 43758.5453123); }\n" + + "float4 main(float2 p)\n" + + "{\n" + + " float fraction = in_progress;\n" + + " float2 fragCoord = p;//sk_FragCoord.xy;\n" + + " float maxDist = in_maxRadius;\n" + + " float fragDist = dist2(in_origin, fragCoord.xy);\n" + + " float circleRadius = maxDist * fraction;\n" + + " float colorVal = (fragDist - circleRadius) / maxDist;\n" + + " float d = fragDist < circleRadius \n" + + " ? 1. - abs(colorVal * 2. * smoothstep(0., 1., fraction)) \n" + + " : 1. - abs(colorVal * 3.);\n" + + " d = smoothstep(0., 1., d);\n" + + " float divider = 2.;\n" + + " float x = floor(fragCoord.x / divider);\n" + + " float y = floor(fragCoord.y / divider);\n" + + " float density = .95;\n" + + " d = rand(float2(x, y)) > density ? d : d * .2;\n" + + " d = d * rand(float2(fraction, x * y));\n" + + " float alpha = 1. - pow(fraction, 3.);\n" + + " return float4(sample(in_paintColor).rgb, d * alpha);\n" + + "}"; + + RippleView(Context c) { + super(c); + setClickable(true); + + mX = CanvasProperty.createFloat(200.0f); + mY = CanvasProperty.createFloat(200.0f); + mRadius = CanvasProperty.createFloat(150.0f); + mProgress = CanvasProperty.createFloat(0.0f); + + Paint p = new Paint(); + p.setAntiAlias(true); + p.setColor(0xFFFF0000); + mPaint = CanvasProperty.createPaint(p); + + mRuntimeShader = new RuntimeShader(sSkSL, false); + mRuntimeShader.setUniform("in_maxRadius", MAX_RADIUS); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (canvas.isHardwareAccelerated()) { + RecordingCanvas recordingCanvas = (RecordingCanvas) canvas; + recordingCanvas.drawRipple(mX, mY, mRadius, mPaint, mProgress, mRuntimeShader); + } + } + + @Override + public boolean performClick() { + for (int i = 0; i < mRunningAnimations.size(); i++) { + mRunningAnimations.get(i).cancel(); + } + mRunningAnimations.clear(); + + mToggle = !mToggle; + + mRunningAnimations.add(new RenderNodeAnimator( + mX, mToggle ? 400.0f : 200.0f)); + + mRunningAnimations.add(new RenderNodeAnimator( + mY, mToggle ? 600.0f : 200.0f)); + + mRunningAnimations.add(new RenderNodeAnimator( + mRadius, mToggle ? MAX_RADIUS : 150.0f)); + + mRunningAnimations.add(new RenderNodeAnimator( + mProgress, mToggle ? 1.0f : 0.0f)); + + mRunningAnimations.add(new RenderNodeAnimator( + mPaint, RenderNodeAnimator.PAINT_ALPHA, 64.0f)); + + // Will be "chained" to run after the above + mRunningAnimations.add(new RenderNodeAnimator( + mPaint, RenderNodeAnimator.PAINT_ALPHA, 255.0f)); + + for (int i = 0; i < mRunningAnimations.size(); i++) { + RenderNodeAnimator anim = mRunningAnimations.get(i); + anim.setDuration(DURATION); + anim.setTarget(this); + if (i == (mRunningAnimations.size() - 1)) { + // "chain" test + anim.setStartValue(64.0f); + anim.setStartDelay(anim.getDuration()); + } + anim.start(); + } + + if (mToggle) { + post(new Runnable() { + @Override + public void run() { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "pretendBusy"); + try { + Thread.sleep(DURATION); + } catch (InterruptedException e) { + } + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + } + }); + } + return true; + } + } +} diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java index 2a601e5cb86d..ad8aac17d844 100644 --- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java +++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java @@ -140,6 +140,21 @@ public class StagedInstallInternalTest { Install.multi(TestApp.AIncompleteSplit, TestApp.B1, TestApp.Apex1).setStaged()); } + @Test + public void testFailStagedSessionIfStagingDirectoryDeleted_Commit() throws Exception { + int sessionId = Install.multi(TestApp.A1, TestApp.Apex1).setStaged().commit(); + assertSessionReady(sessionId); + storeSessionId(sessionId); + } + + @Test + public void testFailStagedSessionIfStagingDirectoryDeleted_Verify() throws Exception { + int sessionId = retrieveLastSessionId(); + PackageInstaller.SessionInfo info = + InstallUtils.getPackageInstaller().getSessionInfo(sessionId); + assertThat(info.isStagedSessionFailed()).isTrue(); + } + private static void assertSessionReady(int sessionId) { assertSessionState(sessionId, (session) -> assertThat(session.isStagedSessionReady()).isTrue()); diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java index 9e1ea2e04528..2201efd3a7ac 100644 --- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java +++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java @@ -279,6 +279,21 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { assertThat(getStagingDirectories()).isEmpty(); } + @Test + public void testFailStagedSessionIfStagingDirectoryDeleted() throws Exception { + // Create a staged session + runPhase("testFailStagedSessionIfStagingDirectoryDeleted_Commit"); + + // Delete the staging directory + getDevice().enableAdbRoot(); + getDevice().executeShellCommand("rm -r /data/app-staging"); + getDevice().disableAdbRoot(); + + getDevice().reboot(); + + runPhase("testFailStagedSessionIfStagingDirectoryDeleted_Verify"); + } + private List<String> getStagingDirectories() throws DeviceNotAvailableException { String baseDir = "/data/app-staging"; try { diff --git a/tests/WindowInsetsTests/res/values/strings.xml b/tests/WindowInsetsTests/res/values/strings.xml index 1a236c6f751d..d6355f5a0464 100644 --- a/tests/WindowInsetsTests/res/values/strings.xml +++ b/tests/WindowInsetsTests/res/values/strings.xml @@ -23,7 +23,7 @@ <!-- The item positions should match the flag values respectively. --> <string-array name="behaviors"> <item>BEHAVIOR_SHOW_BARS_BY_TOUCH</item> - <item>BEHAVIOR_SHOW_BARS_BY_SWIPE</item> + <item>BEHAVIOR_DEFAULT</item> <item>BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE</item> </string-array> </resources> diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java index beb4049cb230..95fd959e5587 100644 --- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java +++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java @@ -53,6 +53,8 @@ public class ControllerActivity extends Activity implements View.OnApplyWindowIn R.array.behaviors, android.R.layout.simple_spinner_item); adapterBehavior.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinnerBehavior.setAdapter(adapterBehavior); + spinnerBehavior.setSelection( + spinnerBehavior.getWindowInsetsController().getSystemBarsBehavior()); spinnerBehavior.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { diff --git a/tests/net/common/java/android/net/CaptivePortalDataTest.kt b/tests/net/common/java/android/net/CaptivePortalDataTest.kt index 2cb16d3372d7..b2bcfeb9019d 100644 --- a/tests/net/common/java/android/net/CaptivePortalDataTest.kt +++ b/tests/net/common/java/android/net/CaptivePortalDataTest.kt @@ -58,7 +58,8 @@ class CaptivePortalDataTest { @Test fun testParcelUnparcel() { - assertParcelSane(data, fieldCount = 8) + val fieldCount = if (SdkLevel.isAtLeastS()) 8 else 7 + assertParcelSane(data, fieldCount) assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build()) assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build()) diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt index 8e1875168a84..16c486562f53 100644 --- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt +++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt @@ -46,8 +46,6 @@ import com.android.server.ConnectivityService import com.android.server.LocalServices import com.android.server.NetworkAgentWrapper import com.android.server.TestNetIdManager -import com.android.server.connectivity.DefaultNetworkMetrics -import com.android.server.connectivity.IpConnectivityMetrics import com.android.server.connectivity.MockableSystemProperties import com.android.server.connectivity.ProxyTracker import com.android.server.net.NetworkPolicyManagerInternal @@ -92,10 +90,6 @@ class ConnectivityServiceIntegrationTest { private lateinit var netd: INetd @Mock private lateinit var dnsResolver: IDnsResolver - @Mock - private lateinit var metricsLogger: IpConnectivityMetrics.Logger - @Mock - private lateinit var defaultMetrics: DefaultNetworkMetrics @Spy private var context = TestableContext(realContext) @@ -149,7 +143,6 @@ class ConnectivityServiceIntegrationTest { @Before fun setUp() { MockitoAnnotations.initMocks(this) - doReturn(defaultMetrics).`when`(metricsLogger).defaultNetworkMetrics() doNothing().`when`(context).sendStickyBroadcastAsUser(any(), any(), any()) networkStackClient = TestNetworkStackClient(realContext) @@ -173,7 +166,6 @@ class ConnectivityServiceIntegrationTest { private fun makeDependencies(): ConnectivityService.Dependencies { val deps = spy(ConnectivityService.Dependencies()) doReturn(networkStackClient).`when`(deps).networkStack - doReturn(metricsLogger).`when`(deps).metricsLogger doReturn(mock(ProxyTracker::class.java)).`when`(deps).makeProxyTracker(any(), any()) doReturn(mock(MockableSystemProperties::class.java)).`when`(deps).systemProperties doReturn(TestNetIdManager()).`when`(deps).makeNetIdManager() diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index a836e81fdb8c..46302698a86c 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.content.Intent.ACTION_USER_ADDED; import static android.content.Intent.ACTION_USER_REMOVED; +import static android.content.Intent.ACTION_USER_UNLOCKED; import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.MATCH_ANY_USER; @@ -221,6 +222,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.security.Credentials; import android.security.KeyStore; import android.system.Os; import android.telephony.TelephonyManager; @@ -237,14 +239,13 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.app.IBatteryStats; import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnInfo; +import com.android.internal.net.VpnProfile; import com.android.internal.util.ArrayUtils; import com.android.internal.util.WakeupMessage; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo; import com.android.server.connectivity.ConnectivityConstants; -import com.android.server.connectivity.DefaultNetworkMetrics; -import com.android.server.connectivity.IpConnectivityMetrics; import com.android.server.connectivity.MockableSystemProperties; import com.android.server.connectivity.Nat464Xlat; import com.android.server.connectivity.NetworkAgentInfo; @@ -281,6 +282,7 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -367,8 +369,6 @@ public class ConnectivityServiceTest { private HandlerThread mAlarmManagerThread; private TestNetIdManager mNetIdManager; - @Mock IpConnectivityMetrics.Logger mMetricsService; - @Mock DefaultNetworkMetrics mDefaultNetworkMetrics; @Mock DeviceIdleInternal mDeviceIdleInternal; @Mock INetworkManagementService mNetworkManagementService; @Mock INetworkStatsService mStatsService; @@ -388,6 +388,7 @@ public class ConnectivityServiceTest { @Mock MockableSystemProperties mSystemProperties; @Mock EthernetManager mEthernetManager; @Mock NetworkPolicyManager mNetworkPolicyManager; + @Mock KeyStore mKeyStore; private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor = ArgumentCaptor.forClass(ResolverParamsParcel.class); @@ -1066,6 +1067,15 @@ public class ConnectivityServiceTest { private int mVpnType = VpnManager.TYPE_VPN_SERVICE; private VpnInfo mVpnInfo; + // These ConditionVariables allow tests to wait for LegacyVpnRunner to be stopped/started. + // TODO: this scheme is ad-hoc and error-prone because it does not fail if, for example, the + // test expects two starts in a row, or even if the production code calls start twice in a + // row. find a better solution. Simply putting a method to create a LegacyVpnRunner into + // Vpn.Dependencies doesn't work because LegacyVpnRunner is not a static class and has + // extensive access into the internals of Vpn. + private ConditionVariable mStartLegacyVpnCv = new ConditionVariable(); + private ConditionVariable mStopVpnRunnerCv = new ConditionVariable(); + public MockVpn(int userId) { super(startHandlerThreadAndReturnLooper(), mServiceContext, new Dependencies() { @@ -1079,7 +1089,7 @@ public class ConnectivityServiceTest { return mDeviceIdleInternal; } }, - mNetworkManagementService, mMockNetd, userId, mock(KeyStore.class)); + mNetworkManagementService, mMockNetd, userId, mKeyStore); } public void setUids(Set<UidRange> uids) { @@ -1191,10 +1201,44 @@ public class ConnectivityServiceTest { } mAgentRegistered = false; setUids(null); + // Remove NET_CAPABILITY_INTERNET or MockNetworkAgent will refuse to connect later on. + mNetworkCapabilities.removeCapability(NET_CAPABILITY_INTERNET); mInterface = null; } @Override + public void startLegacyVpnRunner() { + mStartLegacyVpnCv.open(); + } + + public void expectStartLegacyVpnRunner() { + assertTrue("startLegacyVpnRunner not called after " + TIMEOUT_MS + " ms", + mStartLegacyVpnCv.block(TIMEOUT_MS)); + + // startLegacyVpn calls stopVpnRunnerPrivileged, which will open mStopVpnRunnerCv, just + // before calling startLegacyVpnRunner. Restore mStopVpnRunnerCv, so the test can expect + // that the VpnRunner is stopped and immediately restarted by calling + // expectStartLegacyVpnRunner() and expectStopVpnRunnerPrivileged() back-to-back. + mStopVpnRunnerCv = new ConditionVariable(); + } + + @Override + public void stopVpnRunnerPrivileged() { + if (mVpnRunner != null) { + super.stopVpnRunnerPrivileged(); + disconnect(); + mStartLegacyVpnCv = new ConditionVariable(); + } + mVpnRunner = null; + mStopVpnRunnerCv.open(); + } + + public void expectStopVpnRunnerPrivileged() { + assertTrue("stopVpnRunnerPrivileged not called after " + TIMEOUT_MS + " ms", + mStopVpnRunnerCv.block(TIMEOUT_MS)); + } + + @Override public synchronized VpnInfo getVpnInfo() { if (mVpnInfo != null) return mVpnInfo; @@ -1275,10 +1319,19 @@ public class ConnectivityServiceTest { } } - private static final int VPN_USER = 0; - private static final int APP1_UID = UserHandle.getUid(VPN_USER, 10100); - private static final int APP2_UID = UserHandle.getUid(VPN_USER, 10101); - private static final int VPN_UID = UserHandle.getUid(VPN_USER, 10043); + private static final int PRIMARY_USER = 0; + private static final int APP1_UID = UserHandle.getUid(PRIMARY_USER, 10100); + private static final int APP2_UID = UserHandle.getUid(PRIMARY_USER, 10101); + private static final int VPN_UID = UserHandle.getUid(PRIMARY_USER, 10043); + private static final UserInfo PRIMARY_USER_INFO = new UserInfo(PRIMARY_USER, "", + UserInfo.FLAG_PRIMARY); + + private static final int RESTRICTED_USER = 1; + private static final UserInfo RESTRICTED_USER_INFO = new UserInfo(RESTRICTED_USER, "", + UserInfo.FLAG_RESTRICTED); + static { + RESTRICTED_USER_INFO.restrictedProfileParentId = PRIMARY_USER; + } @Before public void setUp() throws Exception { @@ -1287,12 +1340,14 @@ public class ConnectivityServiceTest { mContext = InstrumentationRegistry.getContext(); MockitoAnnotations.initMocks(this); - when(mMetricsService.defaultNetworkMetrics()).thenReturn(mDefaultNetworkMetrics); - when(mUserManager.getAliveUsers()).thenReturn( - Arrays.asList(new UserInfo[] { - new UserInfo(VPN_USER, "", 0), - })); + when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO)); + when(mUserManager.getUserInfo(PRIMARY_USER)).thenReturn(PRIMARY_USER_INFO); + // canHaveRestrictedProfile does not take a userId. It applies to the userId of the context + // it was started from, i.e., PRIMARY_USER. + when(mUserManager.canHaveRestrictedProfile()).thenReturn(true); + when(mUserManager.getUserInfo(RESTRICTED_USER)).thenReturn(RESTRICTED_USER_INFO); + final ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any())) @@ -1362,9 +1417,9 @@ public class ConnectivityServiceTest { doReturn(mNetworkStack).when(deps).getNetworkStack(); doReturn(mSystemProperties).when(deps).getSystemProperties(); doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any()); - doReturn(mMetricsService).when(deps).getMetricsLogger(); doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt()); doReturn(mBatteryStatsService).when(deps).getBatteryStatsService(); + doReturn(mKeyStore).when(deps).getKeyStore(); doAnswer(inv -> { mPolicyTracker = new WrappedMultinetworkPolicyTracker( inv.getArgument(0), inv.getArgument(1), inv.getArgument(2)); @@ -1533,7 +1588,7 @@ public class ConnectivityServiceTest { waitForIdle(); try { final Intent intent = get(timeoutMs, TimeUnit.MILLISECONDS); - fail("Unexpected broadcast: " + intent.getAction()); + fail("Unexpected broadcast: " + intent.getAction() + " " + intent.getExtras()); } catch (TimeoutException expected) { } finally { mServiceContext.unregisterReceiver(mReceiver); @@ -2074,10 +2129,6 @@ public class ConnectivityServiceTest { @Test public void testOwnerUidCannotChange() throws Exception { - // Owner UIDs are not visible without location permission. - setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION); - final NetworkCapabilities ncTemplate = new NetworkCapabilities(); final int originalOwnerUid = Process.myUid(); ncTemplate.setOwnerUid(originalOwnerUid); @@ -2097,6 +2148,10 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent.setNetworkCapabilities(agentCapabilities, true); waitForIdle(); + // Owner UIDs are not visible without location permission. + setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); + // Check that the capability change has been applied but the owner UID is not modified. NetworkCapabilities nc = mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()); assertEquals(originalOwnerUid, nc.getOwnerUid()); @@ -5837,6 +5892,131 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(callback); } + private void assertGetNetworkInfoOfGetActiveNetworkIsConnected(boolean expectedConnectivity) { + // What Chromium used to do before https://chromium-review.googlesource.com/2605304 + assertEquals("Unexpected result for getActiveNetworkInfo(getActiveNetwork())", + expectedConnectivity, mCm.getNetworkInfo(mCm.getActiveNetwork()).isConnected()); + } + + @Test + public void testVpnUnderlyingNetworkSuspended() throws Exception { + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(callback); + + // Connect a VPN. + mMockVpn.establishForMyUid(false /* validated */, true /* hasInternet */, + false /* isStrictMode */); + callback.expectAvailableCallbacksUnvalidated(mMockVpn); + + // Connect cellular data. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(false /* validated */); + callback.expectCapabilitiesThat(mMockVpn, + nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_CELLULAR)); + callback.assertNoCallback(); + + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertGetNetworkInfoOfGetActiveNetworkIsConnected(true); + + // Suspend the cellular network and expect the VPN to be suspended. + mCellNetworkAgent.suspend(); + callback.expectCapabilitiesThat(mMockVpn, + nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_CELLULAR)); + callback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn); + callback.assertNoCallback(); + + assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED); + assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED); + // VPN's main underlying network is suspended, so no connectivity. + assertGetNetworkInfoOfGetActiveNetworkIsConnected(false); + + // Switch to another network. The VPN should no longer be suspended. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false /* validated */); + callback.expectCapabilitiesThat(mMockVpn, + nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_WIFI)); + + // BUG: the VPN is no longer suspended, so a RESUMED callback should have been sent. + // callback.expectCallback(CallbackEntry.RESUMED, mMockVpn); + callback.assertNoCallback(); + + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED. + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + // BUG: the device has connectivity, so this should return true. + assertGetNetworkInfoOfGetActiveNetworkIsConnected(false); + + // Unsuspend cellular and then switch back to it. + // The same bug happens in the opposite direction: the VPN's capabilities correctly have + // NOT_SUSPENDED, but the VPN's NetworkInfo is in state SUSPENDED. + mCellNetworkAgent.resume(); + callback.assertNoCallback(); + mWiFiNetworkAgent.disconnect(); + callback.expectCapabilitiesThat(mMockVpn, + nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_CELLULAR)); + // Spurious double callback? + callback.expectCapabilitiesThat(mMockVpn, + nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_CELLULAR)); + callback.assertNoCallback(); + + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED. + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + // BUG: the device has connectivity, so this should return true. + assertGetNetworkInfoOfGetActiveNetworkIsConnected(false); + + // Re-suspending the current network fixes the problem. + mCellNetworkAgent.suspend(); + callback.expectCapabilitiesThat(mMockVpn, + nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_CELLULAR)); + callback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn); + callback.assertNoCallback(); + + assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED); + assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED); + assertGetNetworkInfoOfGetActiveNetworkIsConnected(false); + + mCellNetworkAgent.resume(); + callback.expectCapabilitiesThat(mMockVpn, + nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_CELLULAR)); + callback.expectCallback(CallbackEntry.RESUMED, mMockVpn); + callback.assertNoCallback(); + + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertGetNetworkInfoOfGetActiveNetworkIsConnected(true); + } + @Test public void testVpnNetworkActive() throws Exception { final int uid = Process.myUid(); @@ -6315,7 +6495,7 @@ public class ConnectivityServiceTest { } @Test - public void testVpnRestrictedUsers() throws Exception { + public void testRestrictedProfileAffectsVpnUidRanges() throws Exception { // NETWORK_SETTINGS is necessary to see the UID ranges in NetworkCapabilities. mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED); @@ -6347,19 +6527,11 @@ public class ConnectivityServiceTest { callback.expectCapabilitiesThat(mWiFiNetworkAgent, (caps) -> caps.hasCapability(NET_CAPABILITY_VALIDATED)); - // Create a fake restricted profile whose parent is our user ID. - final int userId = UserHandle.getUserId(uid); - when(mUserManager.canHaveRestrictedProfile()).thenReturn(true); - final int restrictedUserId = userId + 1; - final UserInfo info = new UserInfo(restrictedUserId, "user", UserInfo.FLAG_RESTRICTED); - info.restrictedProfileParentId = userId; - assertTrue(info.isRestricted()); - when(mUserManager.getUserInfo(restrictedUserId)).thenReturn(info); - when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, restrictedUserId)) - .thenReturn(UserHandle.getUid(restrictedUserId, VPN_UID)); + when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER)) + .thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID)); final Intent addedIntent = new Intent(ACTION_USER_ADDED); - addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); // Send a USER_ADDED broadcast for it. // The BroadcastReceiver for this broadcast checks that is being run on the handler thread. @@ -6371,7 +6543,7 @@ public class ConnectivityServiceTest { callback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.getUids().size() == 2 && caps.getUids().contains(new UidRange(uid, uid)) - && caps.getUids().contains(UidRange.createForUser(restrictedUserId)) + && caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER)) && caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_WIFI)); @@ -6381,13 +6553,13 @@ public class ConnectivityServiceTest { callback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.getUids().size() == 2 && caps.getUids().contains(new UidRange(uid, uid)) - && caps.getUids().contains(UidRange.createForUser(restrictedUserId)) + && caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER)) && caps.hasTransport(TRANSPORT_VPN) && !caps.hasTransport(TRANSPORT_WIFI)); // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user. final Intent removedIntent = new Intent(ACTION_USER_REMOVED); - removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId); + removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); handler.post(() -> mServiceContext.sendBroadcast(removedIntent)); // Expect that the VPN gains the UID range for the restricted user, and that the capability @@ -6397,53 +6569,72 @@ public class ConnectivityServiceTest { && caps.getUids().contains(new UidRange(uid, uid)) && caps.hasTransport(TRANSPORT_VPN) && !caps.hasTransport(TRANSPORT_WIFI)); + } - // Test lockdown with restricted profiles. + @Test + public void testLockdownVpnWithRestrictedProfiles() throws Exception { + // For ConnectivityService#setAlwaysOnVpnPackage. mServiceContext.setPermission( Manifest.permission.CONTROL_ALWAYS_ON_VPN, PERMISSION_GRANTED); + // For call Vpn#setAlwaysOnPackage. mServiceContext.setPermission( Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED); + // Necessary to see the UID ranges in NetworkCapabilities. mServiceContext.setPermission( Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED); + final NetworkRequest request = new NetworkRequest.Builder() + .removeCapability(NET_CAPABILITY_NOT_VPN) + .build(); + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + + final int uid = Process.myUid(); + // Connect wifi and check that UIDs in the main and restricted profiles have network access. - mMockVpn.disconnect(); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true /* validated */); - final int restrictedUid = UserHandle.getUid(restrictedUserId, 42 /* appId */); + final int restrictedUid = UserHandle.getUid(RESTRICTED_USER, 42 /* appId */); assertNotNull(mCm.getActiveNetworkForUid(uid)); assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); // Enable always-on VPN lockdown. The main user loses network access because no VPN is up. final ArrayList<String> allowList = new ArrayList<>(); - mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList); + mService.setAlwaysOnVpnPackage(PRIMARY_USER, ALWAYS_ON_PACKAGE, true /* lockdown */, + allowList); waitForIdle(); assertNull(mCm.getActiveNetworkForUid(uid)); + // This is arguably overspecified: a UID that is not running doesn't have an active network. + // But it's useful to check that non-default users do not lose network access, and to prove + // that the loss of connectivity below is indeed due to the restricted profile coming up. assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); // Start the restricted profile, and check that the UID within it loses network access. - when(mUserManager.getAliveUsers()).thenReturn( - Arrays.asList(new UserInfo[] { - new UserInfo(userId, "", 0), - info - })); + when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER)) + .thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID)); + when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO, + RESTRICTED_USER_INFO)); // TODO: check that VPN app within restricted profile still has access, etc. + final Intent addedIntent = new Intent(ACTION_USER_ADDED); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); + final Handler handler = new Handler(mCsHandlerThread.getLooper()); handler.post(() -> mServiceContext.sendBroadcast(addedIntent)); waitForIdle(); assertNull(mCm.getActiveNetworkForUid(uid)); assertNull(mCm.getActiveNetworkForUid(restrictedUid)); // Stop the restricted profile, and check that the UID within it has network access again. - when(mUserManager.getAliveUsers()).thenReturn( - Arrays.asList(new UserInfo[] { - new UserInfo(userId, "", 0), - })); + when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO)); + + // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user. + final Intent removedIntent = new Intent(ACTION_USER_REMOVED); + removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); handler.post(() -> mServiceContext.sendBroadcast(removedIntent)); waitForIdle(); assertNull(mCm.getActiveNetworkForUid(uid)); assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); - mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList); + mService.setAlwaysOnVpnPackage(PRIMARY_USER, null, false /* lockdown */, allowList); waitForIdle(); } @@ -6784,6 +6975,7 @@ public class ConnectivityServiceTest { final int userId = UserHandle.getUserId(uid); final ArrayList<String> allowList = new ArrayList<>(); mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList); + waitForIdle(); UidRangeParcel firstHalf = new UidRangeParcel(1, VPN_UID - 1); UidRangeParcel secondHalf = new UidRangeParcel(VPN_UID + 1, 99999); @@ -6805,10 +6997,10 @@ public class ConnectivityServiceTest { // Disable lockdown, expect to see the network unblocked. mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList); - expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf); callback.expectBlockedStatusCallback(false, mWiFiNetworkAgent); defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent); vpnUidCallback.assertNoCallback(); + expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); @@ -6851,9 +7043,11 @@ public class ConnectivityServiceTest { // Disable lockdown, remove our UID from the allowlist, and re-enable lockdown. // Everything should now be blocked. mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList); + waitForIdle(); expectNetworkRejectNonSecureVpn(inOrder, false, piece1, piece2, piece3); allowList.clear(); mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList); + waitForIdle(); expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf); defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent); assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent); @@ -6931,6 +7125,200 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(vpnUidCallback); } + private void setupLegacyLockdownVpn() { + final String profileName = "testVpnProfile"; + final byte[] profileTag = profileName.getBytes(StandardCharsets.UTF_8); + when(mKeyStore.contains(Credentials.LOCKDOWN_VPN)).thenReturn(true); + when(mKeyStore.get(Credentials.LOCKDOWN_VPN)).thenReturn(profileTag); + + final VpnProfile profile = new VpnProfile(profileName); + profile.name = "My VPN"; + profile.server = "192.0.2.1"; + profile.dnsServers = "8.8.8.8"; + profile.type = VpnProfile.TYPE_IPSEC_XAUTH_PSK; + final byte[] encodedProfile = profile.encode(); + when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile); + } + + @Test + public void testLegacyLockdownVpn() throws Exception { + mServiceContext.setPermission( + Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED); + + final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build(); + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + + final TestNetworkCallback defaultCallback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(defaultCallback); + + // Pretend lockdown VPN was configured. + setupLegacyLockdownVpn(); + + // LockdownVpnTracker disables the Vpn teardown code and enables lockdown. + // Check the VPN's state before it does so. + assertTrue(mMockVpn.getEnableTeardown()); + assertFalse(mMockVpn.getLockdown()); + + // Send a USER_UNLOCKED broadcast so CS starts LockdownVpnTracker. + final int userId = UserHandle.getUserId(Process.myUid()); + final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + final Handler handler = new Handler(mCsHandlerThread.getLooper()); + handler.post(() -> mServiceContext.sendBroadcast(addedIntent)); + waitForIdle(); + + // Lockdown VPN disables teardown and enables lockdown. + assertFalse(mMockVpn.getEnableTeardown()); + assertTrue(mMockVpn.getLockdown()); + + // Bring up a network. + // Expect nothing to happen because the network does not have an IPv4 default route: legacy + // VPN only supports IPv4. + final LinkProperties cellLp = new LinkProperties(); + cellLp.setInterfaceName("rmnet0"); + cellLp.addLinkAddress(new LinkAddress("2001:db8::1/64")); + cellLp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, "rmnet0")); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp); + mCellNetworkAgent.connect(false /* validated */); + callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); + defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); + waitForIdle(); + assertNull(mMockVpn.getAgent()); + + // Add an IPv4 address. Ideally the VPN should start, but it doesn't because nothing calls + // LockdownVpnTracker#handleStateChangedLocked. This is a bug. + // TODO: consider fixing this. + cellLp.addLinkAddress(new LinkAddress("192.0.2.2/25")); + cellLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "rmnet0")); + mCellNetworkAgent.sendLinkProperties(cellLp); + callback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); + defaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); + waitForIdle(); + assertNull(mMockVpn.getAgent()); + + // Disconnect, then try again with a network that supports IPv4 at connection time. + // Expect lockdown VPN to come up. + ExpectedBroadcast b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED); + mCellNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + b1.expectBroadcast(); + + // When lockdown VPN is active, the NetworkInfo state in CONNECTIVITY_ACTION is overwritten + // with the state of the VPN network. So expect a CONNECTING broadcast. + b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTING); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp); + mCellNetworkAgent.connect(false /* validated */); + callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); + defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); + b1.expectBroadcast(); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED); + + // TODO: it would be nice if we could simply rely on the production code here, and have + // LockdownVpnTracker start the VPN, have the VPN code register its NetworkAgent with + // ConnectivityService, etc. That would require duplicating a fair bit of code from the + // Vpn tests around how to mock out LegacyVpnRunner. But even if we did that, this does not + // work for at least two reasons: + // 1. In this test, calling registerNetworkAgent does not actually result in an agent being + // registered. This is because nothing calls onNetworkMonitorCreated, which is what + // actually ends up causing handleRegisterNetworkAgent to be called. Code in this test + // that wants to register an agent must use TestNetworkAgentWrapper. + // 2. Even if we exposed Vpn#agentConnect to the test, and made MockVpn#agentConnect call + // the TestNetworkAgentWrapper code, this would deadlock because the + // TestNetworkAgentWrapper code cannot be called on the handler thread since it calls + // waitForIdle(). + mMockVpn.expectStartLegacyVpnRunner(); + b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED); + ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED); + mMockVpn.establishForMyUid(); + callback.expectAvailableThenValidatedCallbacks(mMockVpn); + defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + b1.expectBroadcast(); + b2.expectBroadcast(); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + + // Switch default network from cell to wifi. Expect VPN to disconnect and reconnect. + final LinkProperties wifiLp = new LinkProperties(); + wifiLp.setInterfaceName("wlan0"); + wifiLp.addLinkAddress(new LinkAddress("192.0.2.163/25")); + wifiLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "wlan0")); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp); + + b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED); + // Wifi is CONNECTING because the VPN isn't up yet. + b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTING); + ExpectedBroadcast b3 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED); + mWiFiNetworkAgent.connect(false /* validated */); + b1.expectBroadcast(); + b2.expectBroadcast(); + b3.expectBroadcast(); + mMockVpn.expectStopVpnRunnerPrivileged(); + mMockVpn.expectStartLegacyVpnRunner(); + + // TODO: why is wifi not blocked? Is it because when this callback is sent, the VPN is still + // connected, so the network is not considered blocked by the lockdown UID ranges? But the + // fact that a VPN is connected should only result in the VPN itself being unblocked, not + // any other network. Bug in isUidBlockedByVpn? + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + callback.expectCapabilitiesThat(mMockVpn, nc -> nc.hasTransport(TRANSPORT_WIFI)); + callback.expectCallback(CallbackEntry.LOST, mMockVpn); + defaultCallback.expectCapabilitiesThat(mMockVpn, nc -> nc.hasTransport(TRANSPORT_WIFI)); + defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn); + defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent); + + // While the VPN is reconnecting on the new network, everything is blocked. + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED); + + // The VPN comes up again on wifi. + b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED); + b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); + mMockVpn.establishForMyUid(); + callback.expectAvailableThenValidatedCallbacks(mMockVpn); + defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + b1.expectBroadcast(); + b2.expectBroadcast(); + + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + + // Disconnect cell. Nothing much happens since it's not the default network. + // Whenever LockdownVpnTracker is connected, it will send a connected broadcast any time any + // NetworkInfo is updated. This is probably a bug. + // TODO: consider fixing this. + b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); + mCellNetworkAgent.disconnect(); + b1.expectBroadcast(); + callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + defaultCallback.assertNoCallback(); + + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + + b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED); + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + b1.expectBroadcast(); + callback.expectCapabilitiesThat(mMockVpn, nc -> !nc.hasTransport(TRANSPORT_WIFI)); + b2 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED); + mMockVpn.expectStopVpnRunnerPrivileged(); + callback.expectCallback(CallbackEntry.LOST, mMockVpn); + b2.expectBroadcast(); + } + @Test public final void testLoseTrusted() throws Exception { final NetworkRequest trustedRequest = new NetworkRequest.Builder() @@ -7605,7 +7993,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); // The uid range needs to cover the test app so the network is visible to it. - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); + final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); mMockVpn.establish(lp, VPN_UID, vpnRange); assertVpnUidRangesUpdated(true, vpnRange, VPN_UID); @@ -7633,7 +8021,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); + final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange); assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID); @@ -7649,7 +8037,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun0")); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); // The uid range needs to cover the test app so the network is visible to it. - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); + final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange); assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID); @@ -7664,7 +8052,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); + final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); mMockVpn.establish(lp, VPN_UID, vpnRange); assertVpnUidRangesUpdated(true, vpnRange, VPN_UID); @@ -7716,7 +8104,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. - final UidRange vpnRange = UidRange.createForUser(VPN_USER); + final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER); final Set<UidRange> vpnRanges = Collections.singleton(vpnRange); mMockVpn.establish(lp, VPN_UID, vpnRanges); assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID); @@ -7781,8 +8169,22 @@ public class ConnectivityServiceTest { naExtraInfo.unregister(); } + // To avoid granting location permission bypass. + private void denyAllLocationPrivilegedPermissions() { + mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + PERMISSION_DENIED); + mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, + PERMISSION_DENIED); + mServiceContext.setPermission(Manifest.permission.NETWORK_STACK, + PERMISSION_DENIED); + mServiceContext.setPermission(Manifest.permission.NETWORK_SETUP_WIZARD, + PERMISSION_DENIED); + } + private void setupLocationPermissions( int targetSdk, boolean locationToggle, String op, String perm) throws Exception { + denyAllLocationPrivilegedPermissions(); + final ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.targetSdkVersion = targetSdk; when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any())) @@ -7900,7 +8302,7 @@ public class ConnectivityServiceTest { private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType) throws Exception { - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); + final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange); assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid); mMockVpn.setVpnType(vpnType); @@ -8104,11 +8506,18 @@ public class ConnectivityServiceTest { assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder)); } + public NetworkAgentInfo fakeMobileNai(NetworkCapabilities nc) { + final NetworkInfo info = new NetworkInfo(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_LTE, + ConnectivityManager.getNetworkTypeName(TYPE_MOBILE), + TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE)); + return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(), + nc, 0, mServiceContext, null, new NetworkAgentConfig(), mService, null, null, null, + 0, INVALID_UID); + } + @Test public void testCheckConnectivityDiagnosticsPermissionsNetworkStack() throws Exception { - final NetworkAgentInfo naiWithoutUid = - new NetworkAgentInfo(null, null, null, null, new NetworkCapabilities(), 0, - mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID); + final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities()); mServiceContext.setPermission( android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED); @@ -8121,9 +8530,7 @@ public class ConnectivityServiceTest { @Test public void testCheckConnectivityDiagnosticsPermissionsWrongUidPackageName() throws Exception { - final NetworkAgentInfo naiWithoutUid = - new NetworkAgentInfo(null, null, null, null, new NetworkCapabilities(), 0, - mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID); + final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities()); mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); @@ -8136,9 +8543,7 @@ public class ConnectivityServiceTest { @Test public void testCheckConnectivityDiagnosticsPermissionsNoLocationPermission() throws Exception { - final NetworkAgentInfo naiWithoutUid = - new NetworkAgentInfo(null, null, null, null, new NetworkCapabilities(), 0, - mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID); + final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities()); mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); @@ -8151,22 +8556,17 @@ public class ConnectivityServiceTest { @Test public void testCheckConnectivityDiagnosticsPermissionsActiveVpn() throws Exception { - final Network network = new Network(NET_ID); - final NetworkAgentInfo naiWithoutUid = - new NetworkAgentInfo(null, network, null, null, new NetworkCapabilities(), 0, - mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID); - - setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION); + final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities()); mMockVpn.establishForMyUid(); assertUidRangesUpdatedForMyUid(true); // Wait for networks to connect and broadcasts to be sent before removing permissions. waitForIdle(); - mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); + setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); - assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {network})); + assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {naiWithoutUid.network})); waitForIdle(); assertTrue( "Active VPN permission not applied", @@ -8187,9 +8587,7 @@ public class ConnectivityServiceTest { public void testCheckConnectivityDiagnosticsPermissionsNetworkAdministrator() throws Exception { final NetworkCapabilities nc = new NetworkCapabilities(); nc.setAdministratorUids(new int[] {Process.myUid()}); - final NetworkAgentInfo naiWithUid = - new NetworkAgentInfo(null, null, null, null, nc, 0, mServiceContext, null, null, - mService, null, null, null, 0, INVALID_UID); + final NetworkAgentInfo naiWithUid = fakeMobileNai(nc); setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); @@ -8206,9 +8604,7 @@ public class ConnectivityServiceTest { final NetworkCapabilities nc = new NetworkCapabilities(); nc.setOwnerUid(Process.myUid()); nc.setAdministratorUids(new int[] {Process.myUid()}); - final NetworkAgentInfo naiWithUid = - new NetworkAgentInfo(null, null, null, null, nc, 0, mServiceContext, null, null, - mService, null, null, null, 0, INVALID_UID); + final NetworkAgentInfo naiWithUid = fakeMobileNai(nc); setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); @@ -8475,7 +8871,7 @@ public class ConnectivityServiceTest { lp.setInterfaceName("tun0"); lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); - final UidRange vpnRange = UidRange.createForUser(VPN_USER); + final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER); Set<UidRange> vpnRanges = Collections.singleton(vpnRange); mMockVpn.establish(lp, VPN_UID, vpnRanges); assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID); @@ -8491,4 +8887,20 @@ public class ConnectivityServiceTest { assertVpnUidRangesUpdated(true, newRanges, VPN_UID); assertVpnUidRangesUpdated(false, vpnRanges, VPN_UID); } + + @Test + public void testInvalidRequestTypes() { + final int[] invalidReqTypeInts = new int[] {-1, NetworkRequest.Type.NONE.ordinal(), + NetworkRequest.Type.LISTEN.ordinal(), NetworkRequest.Type.values().length}; + final NetworkCapabilities nc = new NetworkCapabilities().addTransportType(TRANSPORT_WIFI); + + for (int reqTypeInt : invalidReqTypeInts) { + assertThrows("Expect throws for invalid request type " + reqTypeInt, + IllegalArgumentException.class, + () -> mService.requestNetwork(nc, reqTypeInt, null, 0, null, + ConnectivityManager.TYPE_NONE, mContext.getPackageName(), + getAttributionTag()) + ); + } + } } diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java index 96c56e32f156..4d151afecd63 100644 --- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java @@ -34,7 +34,9 @@ import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.IDnsResolver; import android.net.INetd; +import android.net.LinkProperties; import android.net.Network; +import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkProvider; @@ -353,9 +355,10 @@ public class LingerMonitorTest { NetworkCapabilities caps = new NetworkCapabilities(); caps.addCapability(0); caps.addTransportType(transport); - NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info, null, - caps, 50, mCtx, null, null /* config */, mConnService, mNetd, mDnsResolver, mNMS, - NetworkProvider.ID_NONE, Binder.getCallingUid()); + NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info, + new LinkProperties(), caps, 50, mCtx, null, new NetworkAgentConfig() /* config */, + mConnService, mNetd, mDnsResolver, mNMS, NetworkProvider.ID_NONE, + Binder.getCallingUid()); nai.everValidated = true; return nai; } diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp index 3c08d347b19a..c04ddd78e69b 100644 --- a/tests/vcn/Android.bp +++ b/tests/vcn/Android.bp @@ -16,6 +16,7 @@ android_test { "frameworks-base-testutils", "framework-protos", "mockito-target-minus-junit4", + "net-tests-utils", "platform-test-annotations", "services.core", ], diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java new file mode 100644 index 000000000000..9c6b7194af35 --- /dev/null +++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.vcn; + +import static androidx.test.InstrumentationRegistry.getContext; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.net.vcn.VcnManager.VcnUnderlyingNetworkPolicyListener; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.util.concurrent.Executor; + +public class VcnManagerTest { + private static final Executor INLINE_EXECUTOR = Runnable::run; + + private IVcnManagementService mMockVcnManagementService; + private VcnUnderlyingNetworkPolicyListener mMockPolicyListener; + + private Context mContext; + private VcnManager mVcnManager; + + @Before + public void setUp() { + mMockVcnManagementService = mock(IVcnManagementService.class); + mMockPolicyListener = mock(VcnUnderlyingNetworkPolicyListener.class); + + mContext = getContext(); + mVcnManager = new VcnManager(mContext, mMockVcnManagementService); + } + + @Test + public void testAddVcnUnderlyingNetworkPolicyListener() throws Exception { + mVcnManager.addVcnUnderlyingNetworkPolicyListener(INLINE_EXECUTOR, mMockPolicyListener); + + ArgumentCaptor<IVcnUnderlyingNetworkPolicyListener> captor = + ArgumentCaptor.forClass(IVcnUnderlyingNetworkPolicyListener.class); + verify(mMockVcnManagementService).addVcnUnderlyingNetworkPolicyListener(captor.capture()); + + assertTrue(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener)); + + IVcnUnderlyingNetworkPolicyListener listenerWrapper = captor.getValue(); + listenerWrapper.onPolicyChanged(); + verify(mMockPolicyListener).onPolicyChanged(); + } + + @Test + public void testRemoveVcnUnderlyingNetworkPolicyListener() throws Exception { + mVcnManager.addVcnUnderlyingNetworkPolicyListener(INLINE_EXECUTOR, mMockPolicyListener); + + mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); + + assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener)); + verify(mMockVcnManagementService) + .addVcnUnderlyingNetworkPolicyListener( + any(IVcnUnderlyingNetworkPolicyListener.class)); + } + + @Test + public void testRemoveVcnUnderlyingNetworkPolicyListenerUnknownListener() throws Exception { + mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); + + assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener)); + verify(mMockVcnManagementService, never()) + .addVcnUnderlyingNetworkPolicyListener( + any(IVcnUnderlyingNetworkPolicyListener.class)); + } + + @Test(expected = NullPointerException.class) + public void testAddVcnUnderlyingNetworkPolicyListenerNullExecutor() throws Exception { + mVcnManager.addVcnUnderlyingNetworkPolicyListener(null, mMockPolicyListener); + } + + @Test(expected = NullPointerException.class) + public void testAddVcnUnderlyingNetworkPolicyListenerNullListener() throws Exception { + mVcnManager.addVcnUnderlyingNetworkPolicyListener(INLINE_EXECUTOR, null); + } + + @Test(expected = NullPointerException.class) + public void testRemoveVcnUnderlyingNetworkPolicyListenerNullListener() { + mVcnManager.removeVcnUnderlyingNetworkPolicyListener(null); + } +} diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java new file mode 100644 index 000000000000..3ba0a1f53a9f --- /dev/null +++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.vcn; + +import static com.android.testutils.ParcelUtils.assertParcelSane; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import android.net.NetworkCapabilities; + +import org.junit.Test; + +public class VcnUnderlyingNetworkPolicyTest { + private static final VcnUnderlyingNetworkPolicy DEFAULT_NETWORK_POLICY = + new VcnUnderlyingNetworkPolicy( + false /* isTearDownRequested */, new NetworkCapabilities()); + private static final VcnUnderlyingNetworkPolicy SAMPLE_NETWORK_POLICY = + new VcnUnderlyingNetworkPolicy( + true /* isTearDownRequested */, + new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .build()); + + @Test + public void testEquals() { + assertEquals(DEFAULT_NETWORK_POLICY, DEFAULT_NETWORK_POLICY); + assertEquals(SAMPLE_NETWORK_POLICY, SAMPLE_NETWORK_POLICY); + + assertNotEquals(DEFAULT_NETWORK_POLICY, SAMPLE_NETWORK_POLICY); + } + + @Test + public void testParcelUnparcel() { + assertParcelSane(SAMPLE_NETWORK_POLICY, 2); + } +} |