diff options
1034 files changed, 24824 insertions, 7800 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..13c4953715d3 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; } @@ -141,12 +192,25 @@ public final class ApplicationMediaCapabilities implements Parcelable { for (int readCount = 0; readCount < count; ++readCount) { builder.addSupportedVideoMimeType(in.readString()); } + + // Parse unsupported video codec mime types. + count = in.readInt(); + for (int readCount = 0; readCount < count; ++readCount) { + builder.addUnsupportedVideoMimeType(in.readString()); + } + // Parse supported hdr types. count = in.readInt(); for (int readCount = 0; readCount < count; ++readCount) { builder.addSupportedHdrType(in.readString()); } + // Parse unsupported hdr types. + count = in.readInt(); + for (int readCount = 0; readCount < count; ++readCount) { + builder.addUnsupportedHdrType(in.readString()); + } + boolean supported = in.readBoolean(); builder.setSlowMotionSupported(supported); @@ -159,9 +223,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 +232,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 +250,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 +293,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 +385,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 +458,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 +478,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 +491,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 +542,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/config/hiddenapi-temp-blocklist.txt b/config/hiddenapi-max-target-r-loprio.txt index 753bc69be95b..753bc69be95b 100644 --- a/config/hiddenapi-temp-blocklist.txt +++ b/config/hiddenapi-max-target-r-loprio.txt diff --git a/core/api/current.txt b/core/api/current.txt index 8a522d14da9f..06c37d21ed37 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -1055,11 +1055,11 @@ package android { field public static final int parentActivityName = 16843687; // 0x10103a7 field @Deprecated public static final int password = 16843100; // 0x101015c field public static final int path = 16842794; // 0x101002a - field public static final int pathAdvancedPattern = 16844319; // 0x101061f + field public static final int pathAdvancedPattern = 16844320; // 0x1010620 field public static final int pathData = 16843781; // 0x1010405 field public static final int pathPattern = 16842796; // 0x101002c field public static final int pathPrefix = 16842795; // 0x101002b - field public static final int pathSuffix = 16844317; // 0x101061d + field public static final int pathSuffix = 16844318; // 0x101061e field public static final int patternPathData = 16843978; // 0x10104ca field public static final int permission = 16842758; // 0x1010006 field public static final int permissionFlags = 16843719; // 0x10103c7 @@ -1152,7 +1152,7 @@ package android { field public static final int reqNavigation = 16843306; // 0x101022a field public static final int reqTouchScreen = 16843303; // 0x1010227 field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603 - field public static final int requireDeviceScreenOn = 16844316; // 0x101061c + field public static final int requireDeviceScreenOn = 16844317; // 0x101061d field public static final int requireDeviceUnlock = 16843756; // 0x10103ec field public static final int required = 16843406; // 0x101028e field public static final int requiredAccountType = 16843734; // 0x10103d6 @@ -1293,10 +1293,10 @@ package android { field public static final int spotShadowAlpha = 16843967; // 0x10104bf field public static final int src = 16843033; // 0x1010119 field public static final int ssp = 16843747; // 0x10103e3 - field public static final int sspAdvancedPattern = 16844320; // 0x1010620 + field public static final int sspAdvancedPattern = 16844321; // 0x1010621 field public static final int sspPattern = 16843749; // 0x10103e5 field public static final int sspPrefix = 16843748; // 0x10103e4 - field public static final int sspSuffix = 16844318; // 0x101061e + field public static final int sspSuffix = 16844319; // 0x101061f field public static final int stackFromBottom = 16843005; // 0x10100fd field public static final int stackViewStyle = 16843838; // 0x101043e field public static final int starStyle = 16842882; // 0x1010082 @@ -5573,6 +5573,7 @@ package android.app { field public static final String EXTRA_PROGRESS = "android.progress"; field public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate"; field public static final String EXTRA_PROGRESS_MAX = "android.progressMax"; + field public static final String EXTRA_PROMOTE_PICTURE = "android.promotePicture"; field public static final String EXTRA_REMOTE_INPUT_DRAFT = "android.remoteInputDraft"; field public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory"; field @Deprecated public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName"; @@ -5712,12 +5713,13 @@ package android.app { public static class Notification.BigPictureStyle extends android.app.Notification.Style { ctor public Notification.BigPictureStyle(); ctor @Deprecated public Notification.BigPictureStyle(android.app.Notification.Builder); - method public android.app.Notification.BigPictureStyle bigLargeIcon(android.graphics.Bitmap); - method public android.app.Notification.BigPictureStyle bigLargeIcon(android.graphics.drawable.Icon); - method public android.app.Notification.BigPictureStyle bigPicture(android.graphics.Bitmap); + method @NonNull public android.app.Notification.BigPictureStyle bigLargeIcon(@Nullable android.graphics.Bitmap); + method @NonNull public android.app.Notification.BigPictureStyle bigLargeIcon(@Nullable android.graphics.drawable.Icon); + method @NonNull public android.app.Notification.BigPictureStyle bigPicture(@Nullable android.graphics.Bitmap); method @NonNull public android.app.Notification.BigPictureStyle bigPictureContentDescription(@Nullable CharSequence); - method public android.app.Notification.BigPictureStyle setBigContentTitle(CharSequence); - method public android.app.Notification.BigPictureStyle setSummaryText(CharSequence); + method @NonNull public android.app.Notification.BigPictureStyle setBigContentTitle(@Nullable CharSequence); + method @NonNull public android.app.Notification.BigPictureStyle setSummaryText(@Nullable CharSequence); + method @NonNull public android.app.Notification.BigPictureStyle showBigPictureWhenCollapsed(boolean); } public static class Notification.BigTextStyle extends android.app.Notification.Style { @@ -6760,7 +6762,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); @@ -7191,6 +7193,7 @@ package android.app.admin { field public static final String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION"; field public static final String EXTRA_DELEGATION_SCOPES = "android.app.extra.DELEGATION_SCOPES"; field public static final String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN"; + field public static final String EXTRA_DEVICE_PASSWORD_REQUIREMENT_ONLY = "android.app.extra.DEVICE_PASSWORD_REQUIREMENT_ONLY"; field @RequiresPermission(android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY) public static final String EXTRA_PASSWORD_COMPLEXITY = "android.app.extra.PASSWORD_COMPLEXITY"; field public static final String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE"; field public static final String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE"; @@ -8163,6 +8166,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 +10354,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 +10842,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 +12056,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 +12069,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 +15550,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); @@ -21517,6 +21523,8 @@ package android.media { method public void removeKeys(@NonNull byte[]); method public void removeOfflineLicense(@NonNull byte[]); method public void removeSecureStop(@NonNull byte[]); + method public boolean requiresSecureDecoder(@NonNull String); + method public boolean requiresSecureDecoder(@NonNull String, @android.media.MediaDrm.SecurityLevel int); method public void restoreKeys(@NonNull byte[], @NonNull byte[]); method public void setOnEventListener(@Nullable android.media.MediaDrm.OnEventListener); method public void setOnEventListener(@Nullable android.media.MediaDrm.OnEventListener, @Nullable android.os.Handler); @@ -30082,6 +30090,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 +34497,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 +39791,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 +39803,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 +39834,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 { @@ -40182,6 +40213,11 @@ package android.telephony { field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int"; } + public static final class CarrierConfigManager.ImsServiceEntitlement { + field public static final String KEY_AES_URL_STRING = "imsserviceentitlement.aes_url_string"; + field public static final String KEY_PREFIX = "imsserviceentitlement."; + } + public static final class CarrierConfigManager.Iwlan { field public static final int AUTHENTICATION_METHOD_CERT = 1; // 0x1 field public static final int AUTHENTICATION_METHOD_EAP_ONLY = 0; // 0x0 @@ -41119,12 +41155,13 @@ package android.telephony { public class PhoneNumberUtils { ctor public PhoneNumberUtils(); method public static void addTtsSpan(android.text.Spannable, int, int); + method public static boolean areSamePhoneNumber(@NonNull String, @NonNull String, @NonNull String); method @Deprecated public static String calledPartyBCDFragmentToString(byte[], int, int); method public static String calledPartyBCDFragmentToString(byte[], int, int, int); method @Deprecated public static String calledPartyBCDToString(byte[], int, int); method public static String calledPartyBCDToString(byte[], int, int, int); - method public static boolean compare(String, String); - method public static boolean compare(android.content.Context, String, String); + method @Deprecated public static boolean compare(String, String); + method @Deprecated public static boolean compare(android.content.Context, String, String); method public static String convertKeypadLettersToDigits(String); method public static android.text.style.TtsSpan createTtsSpan(String); method public static CharSequence createTtsSpannable(CharSequence); @@ -41309,17 +41346,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 } @@ -41402,6 +41449,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); @@ -41886,6 +41976,7 @@ package android.telephony { field public static final int AUTHTYPE_EAP_SIM = 128; // 0x80 field public static final int CALL_COMPOSER_STATUS_OFF = 0; // 0x0 field public static final int CALL_COMPOSER_STATUS_ON = 1; // 0x1 + field public static final int CALL_COMPOSER_STATUS_ON_NO_PICTURES = 2; // 0x2 field public static final int CALL_STATE_IDLE = 0; // 0x0 field public static final int CALL_STATE_OFFHOOK = 2; // 0x2 field public static final int CALL_STATE_RINGING = 1; // 0x1 @@ -49312,8 +49403,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 } @@ -49374,7 +49466,6 @@ package android.view { field public static final int FLAGS_CHANGED = 4; // 0x4 field public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 1; // 0x1 field public static final int FLAG_ALT_FOCUSABLE_IM = 131072; // 0x20000 - field @Deprecated public static final int FLAG_BLUR_BEHIND = 4; // 0x4 field public static final int FLAG_DIM_BEHIND = 2; // 0x2 field @Deprecated public static final int FLAG_DISMISS_KEYGUARD = 4194304; // 0x400000 field @Deprecated public static final int FLAG_DITHER = 4096; // 0x1000 @@ -51676,6 +51767,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..728affae2a3b 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"; @@ -253,6 +254,7 @@ package android { field public static final String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES"; field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS"; field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY"; + field public static final String USE_BACKGROUND_BLUR = "android.permission.USE_BACKGROUND_BLUR"; field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK"; field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS"; field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS"; @@ -284,6 +286,8 @@ package android { field public static final int sdkVersion = 16844304; // 0x1010610 field public static final int supportsAmbientMode = 16844173; // 0x101058d field public static final int userRestriction = 16844164; // 0x1010584 + field public static final int windowBackgroundBlurEnabled = 16844316; // 0x101061c + field public static final int windowBackgroundBlurRadius = 16844315; // 0x101061b } public static final class R.bool { @@ -369,6 +373,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 +899,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 +1930,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 +1951,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"; @@ -2336,6 +2344,7 @@ package android.content.pm { field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS"; field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio"; field public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub"; + field public static final String FEATURE_CROSS_LAYER_BLUR = "android.software.cross_layer_blur"; field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery"; field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow"; field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock"; @@ -2574,6 +2583,18 @@ package android.debug { } +package android.graphics.drawable { + + public final class BackgroundBlurDrawable extends android.graphics.drawable.Drawable { + ctor @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR) public BackgroundBlurDrawable(); + method public void setBlurRadius(int); + method public void setColor(@ColorInt int); + method public void setCornerRadius(float); + method public void setCornerRadius(float, float, float, float); + } + +} + package android.hardware { public final class Sensor { @@ -5350,9 +5371,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 +7873,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 +10249,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 +10426,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 +10610,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 { @@ -13369,9 +13379,11 @@ package android.view { public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable { method public final long getUserActivityTimeout(); method public final void setUserActivityTimeout(long); + field @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR) public static final int FLAG_BLUR_BEHIND = 4; // 0x4 field @RequiresPermission(android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS) public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000 field @RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW) public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 16; // 0x10 field @RequiresPermission(android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY) public static final int SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY = 8; // 0x8 + field @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR) public int backgroundBlurRadius; } @IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS, android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags { @@ -13445,6 +13457,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 38fac959cdce..9b5a1dda3e63 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/Notification.java b/core/java/android/app/Notification.java index f6a06f3ed5ef..b166d31ce5df 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1198,6 +1198,14 @@ public class Notification implements Parcelable "android.pictureContentDescription"; /** + * {@link #extras} key: this is a boolean to indicate that the + * {@link BigPictureStyle#bigPicture(Bitmap) big picture} is to be shown in the collapsed state + * of a {@link BigPictureStyle} notification. This will replace a + * {@link Builder#setLargeIcon(Icon) large icon} in that state if one was provided. + */ + public static final String EXTRA_PROMOTE_PICTURE = "android.promotePicture"; + + /** * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}. */ @@ -5149,6 +5157,7 @@ public class Notification implements Parcelable // changes are entirely visual, and should otherwise be undetectable by apps. @SuppressWarnings("AndroidFrameworkCompatChange") private void calculateLargeIconDimens(boolean largeIconShown, + @NonNull StandardTemplateParams p, @NonNull TemplateBindResult result) { final Resources resources = mContext.getResources(); final float density = resources.getDisplayMetrics().density; @@ -5159,9 +5168,9 @@ public class Notification implements Parcelable final float viewHeightDp = resources.getDimension( R.dimen.notification_right_icon_size) / density; float viewWidthDp = viewHeightDp; // icons are 1:1 by default - if (largeIconShown && ( - mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S - || DevFlags.shouldBackportSNotifRules(mContext.getContentResolver()))) { + if (largeIconShown && (p.mPromotePicture + || mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S + || DevFlags.shouldBackportSNotifRules(mContext.getContentResolver()))) { Drawable drawable = mN.mLargeIcon.loadDrawable(mContext); if (drawable != null) { int iconWidth = drawable.getIntrinsicWidth(); @@ -5187,7 +5196,7 @@ public class Notification implements Parcelable mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon); } boolean showLargeIcon = mN.mLargeIcon != null && !p.hideLargeIcon; - calculateLargeIconDimens(showLargeIcon, result); + calculateLargeIconDimens(showLargeIcon, p, result); if (showLargeIcon) { contentView.setViewLayoutWidth(R.id.right_icon, result.mRightIconWidthDp, TypedValue.COMPLEX_UNIT_DIP); @@ -6995,6 +7004,7 @@ public class Notification implements Parcelable private Icon mBigLargeIcon; private boolean mBigLargeIconSet = false; private CharSequence mPictureContentDescription; + private boolean mPromotePicture; public BigPictureStyle() { } @@ -7011,7 +7021,8 @@ public class Notification implements Parcelable * Overrides ContentTitle in the big form of the template. * This defaults to the value passed to setContentTitle(). */ - public BigPictureStyle setBigContentTitle(CharSequence title) { + @NonNull + public BigPictureStyle setBigContentTitle(@Nullable CharSequence title) { internalSetBigContentTitle(safeCharSequence(title)); return this; } @@ -7019,7 +7030,8 @@ public class Notification implements Parcelable /** * Set the first line of text after the detail section in the big form of the template. */ - public BigPictureStyle setSummaryText(CharSequence cs) { + @NonNull + public BigPictureStyle setSummaryText(@Nullable CharSequence cs) { internalSetSummaryText(safeCharSequence(cs)); return this; } @@ -7044,22 +7056,36 @@ public class Notification implements Parcelable /** * Provide the bitmap to be used as the payload for the BigPicture notification. */ - public BigPictureStyle bigPicture(Bitmap b) { + @NonNull + public BigPictureStyle bigPicture(@Nullable Bitmap b) { mPicture = b; return this; } /** + * When set, the {@link #bigPicture(Bitmap) big picture} of this style will be promoted and + * shown in place of the {@link Builder#setLargeIcon(Icon) large icon} in the collapsed + * state of this notification. + */ + @NonNull + public BigPictureStyle showBigPictureWhenCollapsed(boolean show) { + mPromotePicture = show; + return this; + } + + /** * Override the large icon when the big notification is shown. */ - public BigPictureStyle bigLargeIcon(Bitmap b) { + @NonNull + public BigPictureStyle bigLargeIcon(@Nullable Bitmap b) { return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null); } /** * Override the large icon when the big notification is shown. */ - public BigPictureStyle bigLargeIcon(Icon icon) { + @NonNull + public BigPictureStyle bigLargeIcon(@Nullable Icon icon) { mBigLargeIconSet = true; mBigLargeIcon = icon; return this; @@ -7112,6 +7138,66 @@ public class Notification implements Parcelable /** * @hide */ + @Override + public RemoteViews makeContentView(boolean increasedHeight) { + if (mPicture == null || !mPromotePicture) { + return super.makeContentView(increasedHeight); + } + + Icon oldLargeIcon = mBuilder.mN.mLargeIcon; + mBuilder.mN.mLargeIcon = Icon.createWithBitmap(mPicture); + // The legacy largeIcon might not allow us to clear the image, as it's taken in + // replacement if the other one is null. Because we're restoring these legacy icons + // for old listeners, this is in general non-null. + Bitmap largeIconLegacy = mBuilder.mN.largeIcon; + mBuilder.mN.largeIcon = null; + + StandardTemplateParams p = mBuilder.mParams.reset() + .viewType(StandardTemplateParams.VIEW_TYPE_NORMAL) + .fillTextsFrom(mBuilder) + .promotePicture(true); + RemoteViews contentView = getStandardView(mBuilder.getBaseLayoutResource(), + p, null /* result */); + + mBuilder.mN.mLargeIcon = oldLargeIcon; + mBuilder.mN.largeIcon = largeIconLegacy; + + return contentView; + } + + /** + * @hide + */ + @Override + public RemoteViews makeHeadsUpContentView(boolean increasedHeight) { + if (mPicture == null || !mPromotePicture) { + return super.makeHeadsUpContentView(increasedHeight); + } + + Icon oldLargeIcon = mBuilder.mN.mLargeIcon; + mBuilder.mN.mLargeIcon = Icon.createWithBitmap(mPicture); + // The legacy largeIcon might not allow us to clear the image, as it's taken in + // replacement if the other one is null. Because we're restoring these legacy icons + // for old listeners, this is in general non-null. + Bitmap largeIconLegacy = mBuilder.mN.largeIcon; + mBuilder.mN.largeIcon = null; + + StandardTemplateParams p = mBuilder.mParams.reset() + .viewType(StandardTemplateParams.VIEW_TYPE_HEADS_UP) + .fillTextsFrom(mBuilder) + .promotePicture(true); + RemoteViews contentView = getStandardView(mBuilder.getHeadsUpBaseLayoutResource(), + p, null /* result */); + + mBuilder.mN.mLargeIcon = oldLargeIcon; + mBuilder.mN.largeIcon = largeIconLegacy; + + return contentView; + } + + /** + * @hide + */ public RemoteViews makeBigContentView() { // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet // This covers the following cases: @@ -7168,6 +7254,7 @@ public class Notification implements Parcelable extras.putCharSequence(EXTRA_PICTURE_CONTENT_DESCRIPTION, mPictureContentDescription); } + extras.putBoolean(EXTRA_PROMOTE_PICTURE, mPromotePicture); extras.putParcelable(EXTRA_PICTURE, mPicture); } @@ -7188,6 +7275,7 @@ public class Notification implements Parcelable extras.getCharSequence(EXTRA_PICTURE_CONTENT_DESCRIPTION); } + mPromotePicture = extras.getBoolean(EXTRA_PROMOTE_PICTURE); mPicture = extras.getParcelable(EXTRA_PICTURE); } @@ -11306,6 +11394,7 @@ public class Notification implements Parcelable boolean mHideTitle; boolean mHideActions; boolean mHideProgress; + boolean mPromotePicture; CharSequence title; CharSequence text; CharSequence headerTextSecondary; @@ -11321,6 +11410,7 @@ public class Notification implements Parcelable mHideTitle = false; mHideActions = false; mHideProgress = false; + mPromotePicture = false; title = null; text = null; summaryText = null; @@ -11360,6 +11450,11 @@ public class Notification implements Parcelable return this; } + final StandardTemplateParams promotePicture(boolean promotePicture) { + this.mPromotePicture = promotePicture; + return this; + } + final StandardTemplateParams title(CharSequence title) { this.title = title; return this; 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..7e8fb91d362f 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1686,6 +1686,20 @@ public class DevicePolicyManager { public static final int PASSWORD_COMPLEXITY_HIGH = 0x50000; /** + * A boolean extra for {@link #ACTION_SET_NEW_PARENT_PROFILE_PASSWORD} requesting that only + * device password requirement is enforced during the parent profile password enrolment flow. + * <p> Normally when enrolling password for the parent profile, both the device-wide password + * requirement (requirement set via {@link #getParentProfileInstance(ComponentName)} instance) + * and the profile password requirement are enforced, if the profile currently does not have a + * separate work challenge. By setting this to {@code true}, profile password requirement is + * explicitly disregarded. + * + * @see #isActivePasswordSufficientForDeviceRequirement() + */ + public static final String EXTRA_DEVICE_PASSWORD_REQUIREMENT_ONLY = + "android.app.extra.DEVICE_PASSWORD_REQUIREMENT_ONLY"; + + /** * @hide */ @Retention(RetentionPolicy.SOURCE) @@ -1700,8 +1714,10 @@ public class DevicePolicyManager { /** * Activity action: have the user enter a new password for the parent profile. * If the intent is launched from within a managed profile, this will trigger - * entering a new password for the parent of the profile. In all other cases - * the behaviour is identical to {@link #ACTION_SET_NEW_PASSWORD}. + * entering a new password for the parent of the profile. The caller can optionally + * set {@link #EXTRA_DEVICE_PASSWORD_REQUIREMENT_ONLY} to only enforce device-wide + * password requirement. In all other cases the behaviour is identical to + * {@link #ACTION_SET_NEW_PASSWORD}. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_SET_NEW_PARENT_PROFILE_PASSWORD @@ -2536,6 +2552,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 +2596,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 +2746,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 +2826,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 { @@ -3732,9 +3860,21 @@ public class DevicePolicyManager { * @hide */ public PasswordMetrics getPasswordMinimumMetrics(@UserIdInt int userHandle) { + return getPasswordMinimumMetrics(userHandle, false); + } + + /** + * Returns minimum PasswordMetrics that satisfies all admin policies. + * If requested, only consider device-wide admin policies and ignore policies set on the + * managed profile instance (as if the managed profile had separate work challenge). + * + * @hide + */ + public PasswordMetrics getPasswordMinimumMetrics(@UserIdInt int userHandle, + boolean deviceWideOnly) { if (mService != null) { try { - return mService.getPasswordMinimumMetrics(userHandle); + return mService.getPasswordMinimumMetrics(userHandle, deviceWideOnly); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -4013,6 +4153,7 @@ public class DevicePolicyManager { * @throws SecurityException if the calling application is not a profile owner of a managed * profile, or if this API is not called on the parent DevicePolicyManager instance. * @throws IllegalStateException if the user isn't unlocked + * @see #EXTRA_DEVICE_PASSWORD_REQUIREMENT_ONLY */ public boolean isActivePasswordSufficientForDeviceRequirement() { if (!mParentInstance) { @@ -4138,12 +4279,25 @@ public class DevicePolicyManager { */ @PasswordComplexity public int getAggregatedPasswordComplexityForUser(int userId) { + return getAggregatedPasswordComplexityForUser(userId, false); + } + + /** + * Returns the password complexity that applies to this user, aggregated from other users if + * necessary (for example, if the DPC has set password complexity requirements on the parent + * profile DPM instance of a managed profile user, they would apply to the primary user on the + * device). If {@code deviceWideOnly} is {@code true}, ignore policies set on the + * managed profile DPM instance (as if the managed profile had separate work challenge). + * @hide + */ + @PasswordComplexity + public int getAggregatedPasswordComplexityForUser(int userId, boolean deviceWideOnly) { if (mService == null) { return PASSWORD_COMPLEXITY_NONE; } try { - return mService.getAggregatedPasswordComplexityForUser(userId); + return mService.getAggregatedPasswordComplexityForUser(userId, deviceWideOnly); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -8521,6 +8675,19 @@ public class DevicePolicyManager { * Called by a profile or device owner to set the permitted input methods services for this * user. By default, the user can use any input method. * <p> + * This method can be called on the {@link DevicePolicyManager} instance, + * returned by {@link #getParentProfileInstance(ComponentName)}, where the caller must be + * a profile owner of an organization-owned device. + * <p> + * If called on the parent instance: + * <ul> + * <li>The permitted input methods will be applied on the personal profile</li> + * <li>Can only permit all input methods (calling this method with a {@code null} package + * list) or only permit system input methods (calling this method with an empty package + * list). This is to prevent the caller from learning which packages are installed on + * the personal side</li> + * </ul> + * <p> * When zero or more packages have been added, input method that are not in the list and not * part of the system can not be enabled by the user. This method will fail if it is called for * a admin that is not for the foreground user or a profile of the foreground user. Any @@ -8535,14 +8702,18 @@ public class DevicePolicyManager { * @param packageNames List of input method package names. * @return {@code true} if the operation succeeded, or {@code false} if the list didn't * contain every enabled non-system input method service. - * @throws SecurityException if {@code admin} is not a device or profile owner. + * @throws SecurityException if {@code admin} is not a device, profile owner or if called on + * the parent profile and the {@code admin} is not a profile owner + * of an organization-owned managed profile. + * @throws IllegalArgumentException if called on the parent profile, the {@code admin} is a + * profile owner of an organization-owned managed profile and the + * list of permitted input method package names is not null or empty. */ public boolean setPermittedInputMethods( @NonNull ComponentName admin, List<String> packageNames) { - throwIfParentInstance("setPermittedInputMethods"); if (mService != null) { try { - return mService.setPermittedInputMethods(admin, packageNames); + return mService.setPermittedInputMethods(admin, packageNames, mParentInstance); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -8554,18 +8725,25 @@ public class DevicePolicyManager { /** * Returns the list of permitted input methods set by this device or profile owner. * <p> + * This method can be called on the {@link DevicePolicyManager} instance, + * returned by {@link #getParentProfileInstance(ComponentName)}, where the caller must be + * a profile owner of an organization-owned managed profile. If called on the parent instance, + * then the returned list of permitted input methods are those which are applied on the + * personal profile. + * <p> * An empty list means no input methods except system input methods are allowed. Null means all * input methods are allowed. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @return List of input method package names. - * @throws SecurityException if {@code admin} is not a device or profile owner. + * @throws SecurityException if {@code admin} is not a device, profile owner or if called on + * the parent profile and the {@code admin} is not a profile owner + * of an organization-owned managed profile. */ public @Nullable List<String> getPermittedInputMethods(@NonNull ComponentName admin) { - throwIfParentInstance("getPermittedInputMethods"); if (mService != null) { try { - return mService.getPermittedInputMethods(admin); + return mService.getPermittedInputMethods(admin, mParentInstance); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -8575,6 +8753,11 @@ public class DevicePolicyManager { /** * Called by the system to check if a specific input method is disabled by admin. + * <p> + * This method can be called on the {@link DevicePolicyManager} instance, + * returned by {@link #getParentProfileInstance(ComponentName)}. If called on the parent + * instance, this method will check whether the given input method is permitted on + * the personal profile. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param packageName Input method package name that needs to be checked. @@ -8587,7 +8770,8 @@ public class DevicePolicyManager { @NonNull String packageName, int userHandle) { if (mService != null) { try { - return mService.isInputMethodPermittedByAdmin(admin, packageName, userHandle); + return mService.isInputMethodPermittedByAdmin(admin, packageName, userHandle, + mParentInstance); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -9823,7 +10007,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 @@ -10674,6 +10858,8 @@ public class DevicePolicyManager { * <li>{@link #setCameraDisabled}</li> * <li>{@link #getCameraDisabled}</li> * <li>{@link #setAccountManagementDisabled(ComponentName, String, boolean)}</li> + * <li>{@link #setPermittedInputMethods}</li> + * <li>{@link #getPermittedInputMethods}</li> * </ul> * * <p>The following methods can be called by the profile owner of a managed profile diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index e855a1cb71ef..8f84bfe0d28a 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -73,7 +73,7 @@ interface IDevicePolicyManager { void setPasswordMinimumNonLetter(in ComponentName who, int length, boolean parent); int getPasswordMinimumNonLetter(in ComponentName who, int userHandle, boolean parent); - PasswordMetrics getPasswordMinimumMetrics(int userHandle); + PasswordMetrics getPasswordMinimumMetrics(int userHandle, boolean deviceWideOnly); void setPasswordHistoryLength(in ComponentName who, int length, boolean parent); int getPasswordHistoryLength(in ComponentName who, int userHandle, boolean parent); @@ -90,7 +90,7 @@ interface IDevicePolicyManager { int getPasswordComplexity(boolean parent); void setRequiredPasswordComplexity(int passwordComplexity, boolean parent); int getRequiredPasswordComplexity(boolean parent); - int getAggregatedPasswordComplexityForUser(int userId); + int getAggregatedPasswordComplexityForUser(int userId, boolean deviceWideOnly); boolean isUsingUnifiedPassword(in ComponentName admin); int getCurrentFailedPasswordAttempts(int userHandle, boolean parent); int getProfileWithMinimumFailedPasswordsForWipe(int userHandle, boolean parent); @@ -232,10 +232,10 @@ interface IDevicePolicyManager { List getPermittedAccessibilityServicesForUser(int userId); boolean isAccessibilityServicePermittedByAdmin(in ComponentName admin, String packageName, int userId); - boolean setPermittedInputMethods(in ComponentName admin,in List packageList); - List getPermittedInputMethods(in ComponentName admin); + boolean setPermittedInputMethods(in ComponentName admin,in List packageList, boolean parent); + List getPermittedInputMethods(in ComponentName admin, boolean parent); List getPermittedInputMethodsForCurrentUser(); - boolean isInputMethodPermittedByAdmin(in ComponentName admin, String packageName, int userId); + boolean isInputMethodPermittedByAdmin(in ComponentName admin, String packageName, int userId, boolean parent); boolean setPermittedCrossProfileNotificationListeners(in ComponentName admin, in List<String> packageList); List<String> getPermittedCrossProfileNotificationListeners(in ComponentName admin); 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..3788257bcf17 100644 --- a/core/java/android/app/role/RoleManager.java +++ b/core/java/android/app/role/RoleManager.java @@ -174,6 +174,11 @@ public final class RoleManager { @NonNull private final Object mListenersLock = new Object(); + @GuardedBy("mRoleControllerManagerLock") + @Nullable + private RoleControllerManager mRoleControllerManager; + private final Object mRoleControllerManagerLock = new Object(); + /** * @hide */ @@ -676,6 +681,54 @@ 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) { + getRoleControllerManager().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) { + getRoleControllerManager().isApplicationVisibleForRole(roleName, packageName, executor, + callback); + } + + @NonNull + private RoleControllerManager getRoleControllerManager() { + synchronized (mRoleControllerManagerLock) { + if (mRoleControllerManager == null) { + mRoleControllerManager = new RoleControllerManager(mContext); + } + return mRoleControllerManager; + } + } + 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/OWNERS b/core/java/android/content/pm/OWNERS index fd32efccbcec..f0def80505ce 100644 --- a/core/java/android/content/pm/OWNERS +++ b/core/java/android/content/pm/OWNERS @@ -6,5 +6,6 @@ patb@google.com per-file PackageParser.java = chiuwinson@google.com per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS +per-file AppSearchPerson.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS per-file UserInfo* = file:/MULTIUSER_OWNERS 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..747f8dc4915c 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. * @@ -3521,6 +3532,17 @@ public abstract class PackageManager { @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_APP_ENUMERATION = "android.software.app_enumeration"; + /** + * Feature for {@link android.view.WindowManager.LayoutParams.backgroundBlurRedius} and + * {@link android.graphics.drawable.BackgroundBlurDrawable}: the device supports cross-layer + * blurring. + * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_CROSS_LAYER_BLUR = "android.software.cross_layer_blur"; + /** @hide */ public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true; 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/CameraStreamStats.java b/core/java/android/hardware/CameraStreamStats.java index ae801b639d51..41d1e2523a9b 100644 --- a/core/java/android/hardware/CameraStreamStats.java +++ b/core/java/android/hardware/CameraStreamStats.java @@ -19,7 +19,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.Log; -import java.util.ArrayList; /** * The camera stream statistics used for passing camera stream information from * camera service to camera service proxy. @@ -30,6 +29,8 @@ import java.util.ArrayList; * @hide */ public class CameraStreamStats implements Parcelable { + public static final int HISTOGRAM_TYPE_UNKNOWN = 0; + public static final int HISTOGRAM_TYPE_CAPTURE_LATENCY = 1; private int mWidth; private int mHeight; @@ -41,6 +42,9 @@ public class CameraStreamStats implements Parcelable { private int mStartLatencyMs; private int mMaxHalBuffers; private int mMaxAppBuffers; + private int mHistogramType; + private float[] mHistogramBins; + private long[] mHistogramCounts; private static final String TAG = "CameraStreamStats"; @@ -55,6 +59,7 @@ public class CameraStreamStats implements Parcelable { mStartLatencyMs = 0; mMaxHalBuffers = 0; mMaxAppBuffers = 0; + mHistogramType = HISTOGRAM_TYPE_UNKNOWN; } public CameraStreamStats(int width, int height, int format, @@ -70,6 +75,7 @@ public class CameraStreamStats implements Parcelable { mStartLatencyMs = startLatencyMs; mMaxHalBuffers = maxHalBuffers; mMaxAppBuffers = maxAppBuffers; + mHistogramType = HISTOGRAM_TYPE_UNKNOWN; } public static final @android.annotation.NonNull Parcelable.Creator<CameraStreamStats> CREATOR = @@ -112,6 +118,9 @@ public class CameraStreamStats implements Parcelable { dest.writeInt(mStartLatencyMs); dest.writeInt(mMaxHalBuffers); dest.writeInt(mMaxAppBuffers); + dest.writeInt(mHistogramType); + dest.writeFloatArray(mHistogramBins); + dest.writeLongArray(mHistogramCounts); } public void readFromParcel(Parcel in) { @@ -125,6 +134,9 @@ public class CameraStreamStats implements Parcelable { mStartLatencyMs = in.readInt(); mMaxHalBuffers = in.readInt(); mMaxAppBuffers = in.readInt(); + mHistogramType = in.readInt(); + mHistogramBins = in.createFloatArray(); + mHistogramCounts = in.createLongArray(); } public int getWidth() { @@ -166,4 +178,16 @@ public class CameraStreamStats implements Parcelable { public int getMaxAppBuffers() { return mMaxAppBuffers; } + + public int getHistogramType() { + return mHistogramType; + } + + public float[] getHistogramBins() { + return mHistogramBins; + } + + public long[] getHistogramCounts() { + return mHistogramCounts; + } } 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/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java index b09eda4ad2b6..78584990e75e 100644 --- a/core/java/android/hardware/hdmi/HdmiControlManager.java +++ b/core/java/android/hardware/hdmi/HdmiControlManager.java @@ -454,6 +454,33 @@ public final class HdmiControlManager { @Retention(RetentionPolicy.SOURCE) public @interface SystemAudioModeMuting {} + // -- Whether the HDMI CEC volume control is enabled or disabled. + /** + * HDMI CEC enabled. + * + * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE + * @hide + */ + public static final int VOLUME_CONTROL_ENABLED = 1; + /** + * HDMI CEC disabled. + * + * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE + * @hide + */ + public static final int VOLUME_CONTROL_DISABLED = 0; + /** + * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE + * @hide + */ + @IntDef({ + VOLUME_CONTROL_ENABLED, + VOLUME_CONTROL_DISABLED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface VolumeControl {} + + // -- Settings available in the CEC Configuration. /** * Name of a setting deciding whether the CEC is enabled. @@ -492,6 +519,43 @@ public final class HdmiControlManager { @SystemApi public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING = "system_audio_mode_muting"; + + /** + * Controls whether volume control commands via HDMI CEC are enabled. + * + * <p>Effects on different device types: + * <table> + * <tr><th>HDMI CEC device type</th><th>0: disabled</th><th>1: enabled</th></tr> + * <tr> + * <td>TV (type: 0)</td> + * <td>Per CEC specification.</td> + * <td>TV changes system volume. TV no longer reacts to incoming volume changes + * via {@code <User Control Pressed>}. TV no longer handles {@code <Report Audio + * Status>}.</td> + * </tr> + * <tr> + * <td>Playback device (type: 4)</td> + * <td>Device sends volume commands to TV/Audio system via {@code <User Control + * Pressed>}</td> + * <td>Device does not send volume commands via {@code <User Control Pressed>}.</td> + * </tr> + * <tr> + * <td>Audio device (type: 5)</td> + * <td>Full "System Audio Control" capabilities.</td> + * <td>Audio device no longer reacts to incoming {@code <User Control Pressed>} + * volume commands. Audio device no longer reports volume changes via {@code + * <Report Audio Status>}.</td> + * </tr> + * </table> + * + * <p> Due to the resulting behavior, usage on TV and Audio devices is discouraged. + * + * @hide + * @see android.hardware.hdmi.HdmiControlManager#setHdmiCecVolumeControlEnabled(int) + */ + public static final String CEC_SETTING_NAME_VOLUME_CONTROL_MODE = + "volume_control_enabled"; + /** * @hide */ @@ -501,6 +565,7 @@ public final class HdmiControlManager { CEC_SETTING_NAME_POWER_CONTROL_MODE, CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, + CEC_SETTING_NAME_VOLUME_CONTROL_MODE, }) public @interface CecSettingName {} @@ -913,14 +978,16 @@ public final class HdmiControlManager { * * <p> Due to the resulting behavior, usage on TV and Audio devices is discouraged. * - * @param isHdmiCecVolumeControlEnabled target state of HDMI CEC volume control. - * @see Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED + * @param hdmiCecVolumeControlEnabled target state of HDMI CEC volume control. + * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE * @hide */ @RequiresPermission(android.Manifest.permission.HDMI_CEC) - public void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled) { + public void setHdmiCecVolumeControlEnabled( + @VolumeControl int hdmiCecVolumeControlEnabled) { try { - mService.setHdmiCecVolumeControlEnabled(isHdmiCecVolumeControlEnabled); + mService.setCecSettingIntValue(CEC_SETTING_NAME_VOLUME_CONTROL_MODE, + hdmiCecVolumeControlEnabled); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -931,9 +998,10 @@ public final class HdmiControlManager { * @hide */ @RequiresPermission(android.Manifest.permission.HDMI_CEC) - public boolean isHdmiCecVolumeControlEnabled() { + @VolumeControl + public int getHdmiCecVolumeControlEnabled() { try { - return mService.isHdmiCecVolumeControlEnabled(); + return mService.getCecSettingIntValue(CEC_SETTING_NAME_VOLUME_CONTROL_MODE); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1042,7 +1110,8 @@ public final class HdmiControlManager { * * Note: Value of isCecAvailable is only valid when isCecEnabled is true. **/ - void onStatusChange(boolean isCecEnabled, boolean isCecAvailable); + void onStatusChange(@HdmiControlManager.HdmiCecControl int isCecEnabled, + boolean isCecAvailable); } private final ArrayMap<HdmiControlStatusChangeListener, IHdmiControlStatusChangeListener> @@ -1056,10 +1125,10 @@ public final class HdmiControlManager { /** * Called when the HDMI Control (CEC) volume control feature is enabled/disabled. * - * @param enabled status of HDMI CEC volume control feature - * @see {@link HdmiControlManager#setHdmiCecVolumeControlEnabled(boolean)} ()} + * @param hdmiCecVolumeControl status of HDMI CEC volume control feature + * @see {@link HdmiControlManager#setHdmiCecVolumeControlEnabled(int)} ()} **/ - void onHdmiCecVolumeControlFeature(boolean enabled); + void onHdmiCecVolumeControlFeature(@VolumeControl int hdmiCecVolumeControl); } private final ArrayMap<HdmiCecVolumeControlFeatureListener, @@ -1283,7 +1352,7 @@ public final class HdmiControlManager { Executor executor, final HdmiControlStatusChangeListener listener) { return new IHdmiControlStatusChangeListener.Stub() { @Override - public void onStatusChange(boolean isCecEnabled, boolean isCecAvailable) { + public void onStatusChange(@HdmiCecControl int isCecEnabled, boolean isCecAvailable) { final long token = Binder.clearCallingIdentity(); try { executor.execute(() -> listener.onStatusChange(isCecEnabled, isCecAvailable)); @@ -1360,7 +1429,7 @@ public final class HdmiControlManager { Executor executor, final HdmiCecVolumeControlFeatureListener listener) { return new android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener.Stub() { @Override - public void onHdmiCecVolumeControlFeature(boolean enabled) { + public void onHdmiCecVolumeControlFeature(int enabled) { final long token = Binder.clearCallingIdentity(); try { executor.execute(() -> listener.onHdmiCecVolumeControlFeature(enabled)); diff --git a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java index 89a7afa894eb..9a9e945a91cf 100644 --- a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java +++ b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java @@ -273,17 +273,6 @@ public final class HdmiControlServiceWrapper { } @Override - public void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled) { - HdmiControlServiceWrapper.this.setHdmiCecVolumeControlEnabled( - isHdmiCecVolumeControlEnabled); - } - - @Override - public boolean isHdmiCecVolumeControlEnabled() { - return HdmiControlServiceWrapper.this.isHdmiCecVolumeControlEnabled(); - } - - @Override public void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute) { HdmiControlServiceWrapper.this.reportAudioStatus(deviceType, volume, maxVolume, isMute); } diff --git a/core/java/android/hardware/hdmi/IHdmiCecVolumeControlFeatureListener.aidl b/core/java/android/hardware/hdmi/IHdmiCecVolumeControlFeatureListener.aidl index 873438bb1d20..f7c5887864c9 100644 --- a/core/java/android/hardware/hdmi/IHdmiCecVolumeControlFeatureListener.aidl +++ b/core/java/android/hardware/hdmi/IHdmiCecVolumeControlFeatureListener.aidl @@ -26,7 +26,7 @@ oneway interface IHdmiCecVolumeControlFeatureListener { * Called when the HDMI Control (CEC) volume control feature is enabled/disabled. * * @param enabled status of HDMI CEC volume control feature - * @see {@link HdmiControlManager#setHdmiCecVolumeControlEnabled(boolean)} ()} + * @see {@link HdmiControlManager#setHdmiCecVolumeControlEnabled(int)} ()} **/ - void onHdmiCecVolumeControlFeature(boolean enabled); + void onHdmiCecVolumeControlFeature(int hdmiCecVolumeControl); } diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl index d7329e0ba3b5..7f0e53ea2e68 100644 --- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl +++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl @@ -86,8 +86,6 @@ interface IHdmiControlService { void sendMhlVendorCommand(int portId, int offset, int length, in byte[] data); void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener); void setStandbyMode(boolean isStandbyModeOn); - void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled); - boolean isHdmiCecVolumeControlEnabled(); void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute); void setSystemAudioModeOnForAudioOnlySource(); void addCecSettingChangeListener(String name, IHdmiCecSettingChangeListener listener); diff --git a/core/java/android/hardware/hdmi/IHdmiControlStatusChangeListener.aidl b/core/java/android/hardware/hdmi/IHdmiControlStatusChangeListener.aidl index 889d3fe1fae1..d61ab6ac2da7 100644 --- a/core/java/android/hardware/hdmi/IHdmiControlStatusChangeListener.aidl +++ b/core/java/android/hardware/hdmi/IHdmiControlStatusChangeListener.aidl @@ -28,11 +28,11 @@ oneway interface IHdmiControlStatusChangeListener { * Called when HDMI Control (CEC) is enabled/disabled. * * @param isCecEnabled status of HDMI Control - * {@link android.provider.Settings.Global#HDMI_CONTROL_ENABLED}: {@code true} if enabled. + * {@link android.hardware.hdmi.HdmiControlManager#CEC_SETTING_NAME_HDMI_CEC_ENABLED}: {@link android.hardware.hdmi.HdmiControlManager#HDMI_CEC_CONTROL_ENABLED} if enabled. * @param isCecAvailable status of CEC support of the connected display (the TV). * {@code true} if supported. * - * Note: Value of isCecAvailable is only valid when isCecEnabled is true. + * Note: Value of isCecAvailable is only valid when isCecEnabled is {@link android.hardware.hdmi.HdmiControlManager#HDMI_CEC_CONTROL_ENABLED}. **/ - void onStatusChange(boolean isCecEnabled, boolean isCecAvailable); + void onStatusChange(int isCecEnabled, boolean isCecAvailable); } 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/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index df9a7c2cb586..44a2e97e6f04 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -410,6 +410,11 @@ public class InputMethodService extends AbstractInputMethodService { private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT; private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_ADJUST_NOTHING; + /** + * Timeout after which hidden IME surface will be removed from memory + */ + private static final long TIMEOUT_SURFACE_REMOVAL_MILLIS = 5000; + InputMethodManager mImm; private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations(); @@ -506,6 +511,8 @@ public class InputMethodService extends AbstractInputMethodService { private boolean mAutomotiveHideNavBarForKeyboard; private boolean mIsAutomotive; + private Handler mHandler; + private boolean mImeSurfaceScheduledForRemoval; /** * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput} @@ -903,11 +910,31 @@ public class InputMethodService extends AbstractInputMethodService { requestHideSelf(0); } + private void scheduleImeSurfaceRemoval() { + if (mShowInputRequested || mWindowVisible || mWindow == null + || mImeSurfaceScheduledForRemoval) { + return; + } + if (mHandler == null) { + mHandler = new Handler(getMainLooper()); + } + mImeSurfaceScheduledForRemoval = true; + mHandler.postDelayed(() -> removeImeSurface(), TIMEOUT_SURFACE_REMOVAL_MILLIS); + } + private void removeImeSurface() { - if (!mShowInputRequested && !mWindowVisible) { - // hiding a window removes its surface. + // hiding a window removes its surface. + if (mWindow != null) { mWindow.hide(); } + mImeSurfaceScheduledForRemoval = false; + } + + private void cancelImeSurfaceRemoval() { + if (mHandler != null && mImeSurfaceScheduledForRemoval) { + mHandler.removeCallbacksAndMessages(null /* token */); + mImeSurfaceScheduledForRemoval = false; + } } private void setImeWindowStatus(int visibilityFlags, int backDisposition) { @@ -1043,7 +1070,7 @@ public class InputMethodService extends AbstractInputMethodService { * @hide */ public final void removeImeSurface() { - InputMethodService.this.removeImeSurface(); + InputMethodService.this.scheduleImeSurfaceRemoval(); } } @@ -2271,6 +2298,9 @@ public class InputMethodService extends AbstractInputMethodService { ImeTracing.getInstance().triggerServiceDump( "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", this, null /* icProto */); + if (setVisible) { + cancelImeSurfaceRemoval(); + } mPrivOps.applyImeVisibility(setVisible ? mCurShowInputToken : mCurHideInputToken, setVisible); } 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/VcnTransportInfo.java b/core/java/android/net/vcn/VcnTransportInfo.java new file mode 100644 index 000000000000..4d8cf91621ba --- /dev/null +++ b/core/java/android/net/vcn/VcnTransportInfo.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.vcn; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.TransportInfo; +import android.net.wifi.WifiInfo; +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.SubscriptionManager; + +import java.util.Objects; + +/** + * VcnTransportInfo contains information about the VCN's underlying transports for SysUi. + * + * <p>Presence of this class in the NetworkCapabilities.TransportInfo implies that the network is a + * VCN. + * + * <p>VcnTransportInfo must exist on top of either an underlying Wifi or Cellular Network. If the + * underlying Network is WiFi, the subId will be {@link + * SubscriptionManager#INVALID_SUBSCRIPTION_ID}. If the underlying Network is Cellular, the WifiInfo + * will be {@code null}. + * + * @hide + */ +public class VcnTransportInfo implements TransportInfo, Parcelable { + @Nullable private final WifiInfo mWifiInfo; + private final int mSubId; + + public VcnTransportInfo(@NonNull WifiInfo wifiInfo) { + this(wifiInfo, SubscriptionManager.INVALID_SUBSCRIPTION_ID); + } + + public VcnTransportInfo(int subId) { + this(null /* wifiInfo */, subId); + } + + private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId) { + if (wifiInfo == null && subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + throw new IllegalArgumentException( + "VcnTransportInfo requires either non-null WifiInfo or valid subId"); + } + + mWifiInfo = wifiInfo; + mSubId = subId; + } + + /** + * Get the {@link WifiInfo} for this VcnTransportInfo. + * + * <p>If the underlying Network for the associated VCN is Cellular, returns null. + * + * @return the WifiInfo if there is an underlying WiFi connection, else null. + */ + @Nullable + public WifiInfo getWifiInfo() { + return mWifiInfo; + } + + /** + * Get the subId for the VCN Network associated with this VcnTransportInfo. + * + * <p>If the underlying Network for the associated VCN is WiFi, returns {@link + * SubscriptionManager#INVALID_SUBSCRIPTION_ID}. + * + * @return the Subscription ID if a cellular underlying Network is present, else {@link + * android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID}. + */ + public int getSubId() { + return mSubId; + } + + @Override + public int hashCode() { + return Objects.hash(mWifiInfo, mSubId); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof VcnTransportInfo)) return false; + final VcnTransportInfo that = (VcnTransportInfo) o; + + return Objects.equals(mWifiInfo, that.mWifiInfo) && mSubId == that.mSubId; + } + + /** {@inheritDoc} */ + @Override + public int describeContents() { + return 0; + } + + /** {@inheritDoc} */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) {} + + /** Implement the Parcelable interface */ + public static final @NonNull Creator<VcnTransportInfo> CREATOR = + new Creator<VcnTransportInfo>() { + public VcnTransportInfo createFromParcel(Parcel in) { + // return null instead of a default VcnTransportInfo to avoid leaking + // information about this being a VCN Network (instead of macro cellular, etc) + return null; + } + + public VcnTransportInfo[] newArray(int size) { + return new VcnTransportInfo[size]; + } + }; +} 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/SystemVibrator.java b/core/java/android/os/SystemVibrator.java index 0330500f0997..30afe38be397 100644 --- a/core/java/android/os/SystemVibrator.java +++ b/core/java/android/os/SystemVibrator.java @@ -17,6 +17,7 @@ package android.os; import android.annotation.CallbackExecutor; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; @@ -27,6 +28,8 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Objects; import java.util.concurrent.Executor; @@ -38,6 +41,14 @@ import java.util.concurrent.Executor; public class SystemVibrator extends Vibrator { private static final String TAG = "Vibrator"; + private static final int VIBRATOR_PRESENT_UNKNOWN = 0; + private static final int VIBRATOR_PRESENT_YES = 1; + private static final int VIBRATOR_PRESENT_NO = 2; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({VIBRATOR_PRESENT_UNKNOWN, VIBRATOR_PRESENT_YES, VIBRATOR_PRESENT_NO}) + private @interface VibratorPresent {} + private final IVibratorService mService; private final IVibratorManagerService mManagerService; private final Object mLock = new Object(); @@ -45,6 +56,9 @@ public class SystemVibrator extends Vibrator { private final Context mContext; @GuardedBy("mLock") private VibratorInfo mVibratorInfo; + @GuardedBy("mLock") + @VibratorPresent + private int mVibratorPresent; @GuardedBy("mDelegates") private final ArrayMap<OnVibratorStateChangedListener, @@ -69,15 +83,18 @@ public class SystemVibrator extends Vibrator { @Override public boolean hasVibrator() { - if (mService == null) { - Log.w(TAG, "Failed to vibrate; no vibrator service."); - return false; - } try { - return mService.hasVibrator(); + synchronized (mLock) { + if (mVibratorPresent == VIBRATOR_PRESENT_UNKNOWN && mService != null) { + mVibratorPresent = + mService.hasVibrator() ? VIBRATOR_PRESENT_YES : VIBRATOR_PRESENT_NO; + } + return mVibratorPresent == VIBRATOR_PRESENT_YES; + } } catch (RemoteException e) { + Log.w(TAG, "Failed to query vibrator presence", e); + return false; } - return 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..04fe8978788b 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; @@ -9802,6 +9802,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + private void notifyAttachForDrawables() { + if (mBackground != null) mBackground.onAttached(this); + if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) { + mForegroundInfo.mDrawable.onAttached(this); + } + } + + private void notifyDetachForDrawables() { + if (mBackground != null) mBackground.onDetached(this); + if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) { + mForegroundInfo.mDrawable.onDetached(this); + } + } + private void setNotifiedContentCaptureAppeared() { mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED; mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED; @@ -20653,6 +20667,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, notifyEnterOrExitForAutoFillIfNeeded(true); notifyAppearedOrDisappearedForContentCaptureIfNeeded(true); + notifyAttachForDrawables(); } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) @@ -20702,6 +20717,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, notifyEnterOrExitForAutoFillIfNeeded(false); notifyAppearedOrDisappearedForContentCaptureIfNeeded(false); + notifyDetachForDrawables(); } /** @@ -23830,6 +23846,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mBackground != null) { if (isAttachedToWindow()) { mBackground.setVisible(false, false); + mBackground.onDetached(this); } mBackground.setCallback(null); unscheduleDrawable(mBackground); @@ -23879,6 +23896,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, background.setState(getDrawableState()); } if (isAttachedToWindow()) { + background.onAttached(this); background.setVisible(getWindowVisibility() == VISIBLE && isShown(), false); } @@ -24111,6 +24129,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mForegroundInfo.mDrawable != null) { if (isAttachedToWindow()) { mForegroundInfo.mDrawable.setVisible(false, false); + mForegroundInfo.mDrawable.onDetached(this); } mForegroundInfo.mDrawable.setCallback(null); unscheduleDrawable(mForegroundInfo.mDrawable); @@ -24128,6 +24147,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } applyForegroundTint(); if (isAttachedToWindow()) { + foreground.onAttached(this); foreground.setVisible(getWindowVisibility() == VISIBLE && isShown(), false); } // Set callback last, since the view may still be initializing. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7e0ebbcc61d2..d6949623f377 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; @@ -117,6 +115,7 @@ import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.Region; import android.graphics.RenderNode; +import android.graphics.drawable.BackgroundBlurDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.hardware.display.DisplayManager; @@ -188,7 +187,6 @@ import android.window.ClientWindowFrames; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.graphics.drawable.BackgroundBlurDrawable; import com.android.internal.inputmethod.InputMethodDebug; import com.android.internal.os.IResultReceiver; import com.android.internal.os.SomeArgs; @@ -672,6 +670,14 @@ public final class ViewRootImpl implements ViewParent, new BackgroundBlurDrawable.Aggregator(this); /** + * @return {@link BackgroundBlurDrawable.Aggregator} for this instance. + */ + @NonNull + public BackgroundBlurDrawable.Aggregator getBlurRegionAggregator() { + return mBlurRegionAggregator; + } + + /** * @return {@link ImeFocusController} for this instance. */ @NonNull @@ -2167,10 +2173,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 +9158,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 +9173,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()) { @@ -10048,13 +10054,6 @@ public final class ViewRootImpl implements ViewParent, } } - /** - * Creates a background blur drawable for the backing {@link Surface}. - */ - public BackgroundBlurDrawable createBackgroundBlurDrawable() { - return mBlurRegionAggregator.createBackgroundBlurDrawable(mContext); - } - @Override public void onDescendantUnbufferedRequested() { mUnbufferedInputSource = mView.mUnbufferedInputSource; 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..c2d990a40fe4 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 @@ -1498,9 +1508,13 @@ public interface WindowManager extends ViewManager { * Use {@link #dimAmount} to control the amount of dim. */ public static final int FLAG_DIM_BEHIND = 0x00000002; - /** Window flag: blur everything behind this window. - * @deprecated Blurring is no longer supported. */ - @Deprecated + /** Window flag: enable blurring behind this window. + * To set the amount of blur, use {@link #backgroundBlurRadius} + * + * @hide + */ + @RequiresPermission(permission.USE_BACKGROUND_BLUR) + @SystemApi public static final int FLAG_BLUR_BEHIND = 0x00000004; /** Window flag: this window won't ever get key input focus, so the @@ -3215,10 +3229,14 @@ public interface WindowManager extends ViewManager { public boolean preferMinimalPostProcessing = false; /** - * Indicates that this window wants to have blurred content behind it. + * When {@link FLAG_BLUR_BEHIND} is set, this is the amount of blur in pixels that this + * window will use to blur behind itself. + * The range is from 0, which means no blur, to 150. * * @hide */ + @SystemApi + @RequiresPermission(permission.USE_BACKGROUND_BLUR) public int backgroundBlurRadius = 0; /** 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/OWNERS b/core/java/android/view/textclassifier/OWNERS index ac80d9f4cdd0..4bcdeea472e3 100644 --- a/core/java/android/view/textclassifier/OWNERS +++ b/core/java/android/view/textclassifier/OWNERS @@ -6,3 +6,5 @@ svetoslavganov@android.com svetoslavganov@google.com augale@google.com joannechung@google.com +tonymak@google.com +licha@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/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 141dc79f4c93..3be841cb2431 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -2540,8 +2540,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } - params.backgroundBlurRadius = a.getDimensionPixelSize( - R.styleable.Window_windowBackgroundBlurRadius, 0); + if (a.getBoolean(R.styleable.Window_windowBackgroundBlurEnabled, false)) { + if ((getForcedWindowFlags() & WindowManager.LayoutParams.FLAG_BLUR_BEHIND) == 0) { + params.flags |= WindowManager.LayoutParams.FLAG_BLUR_BEHIND; + } + + params.backgroundBlurRadius = a.getDimensionPixelSize( + android.R.styleable.Window_windowBackgroundBlurRadius, 0); + } if (params.windowAnimations == 0) { params.windowAnimations = a.getResourceId( 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/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 9712b4e794c5..a161f18b2aab 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -307,49 +307,47 @@ public class LockPatternUtils { return getDevicePolicyManager().getPasswordMaximumLength(quality); } + /** + * Returns aggregated (legacy) password quality requirement on the target user from all admins. + */ public PasswordMetrics getRequestedPasswordMetrics(int userId) { - return getDevicePolicyManager().getPasswordMinimumMetrics(userId); + return getRequestedPasswordMetrics(userId, false); } /** - * Returns the effective complexity for the user. - * @param userId The user to return the complexity for. - * @return complexity level for the user. + * Returns aggregated (legacy) password quality requirement on the target user from all admins, + * optioanlly disregarding policies set on the managed profile as if the profile had separate + * work challenge. */ - public @DevicePolicyManager.PasswordComplexity int getRequestedPasswordComplexity(int userId) { - return getDevicePolicyManager().getAggregatedPasswordComplexityForUser(userId); - } - - public int getRequestedPasswordQuality(int userId) { - return getDevicePolicyManager().getPasswordQuality(null, userId); + public PasswordMetrics getRequestedPasswordMetrics(int userId, boolean deviceWideOnly) { + return getDevicePolicyManager().getPasswordMinimumMetrics(userId, deviceWideOnly); } private int getRequestedPasswordHistoryLength(int userId) { return getDevicePolicyManager().getPasswordHistoryLength(null, userId); } - public int getRequestedPasswordMinimumLetters(int userId) { - return getDevicePolicyManager().getPasswordMinimumLetters(null, userId); - } - - public int getRequestedPasswordMinimumUpperCase(int userId) { - return getDevicePolicyManager().getPasswordMinimumUpperCase(null, userId); - } - - public int getRequestedPasswordMinimumLowerCase(int userId) { - return getDevicePolicyManager().getPasswordMinimumLowerCase(null, userId); - } - - public int getRequestedPasswordMinimumNumeric(int userId) { - return getDevicePolicyManager().getPasswordMinimumNumeric(null, userId); + /** + * Returns the effective complexity for the user. + * @param userId The user to return the complexity for. + * @return complexity level for the user. + */ + public @DevicePolicyManager.PasswordComplexity int getRequestedPasswordComplexity(int userId) { + return getRequestedPasswordComplexity(userId, false); } - public int getRequestedPasswordMinimumSymbols(int userId) { - return getDevicePolicyManager().getPasswordMinimumSymbols(null, userId); - } + /** + * Returns the effective complexity for the user, optioanlly disregarding complexity set on the + * managed profile as if the profile had separate work challenge. - public int getRequestedPasswordMinimumNonLetter(int userId) { - return getDevicePolicyManager().getPasswordMinimumNonLetter(null, userId); + * @param userId The user to return the complexity for. + * @param deviceWideOnly whether to ignore complexity set on the managed profile. + * @return complexity level for the user. + */ + public @DevicePolicyManager.PasswordComplexity int getRequestedPasswordComplexity(int userId, + boolean deviceWideOnly) { + return getDevicePolicyManager().getAggregatedPasswordComplexityForUser(userId, + deviceWideOnly); } @UnsupportedAppUsage diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS index cca39ea3287d..ae566c3988cd 100644 --- a/core/java/com/android/internal/widget/OWNERS +++ b/core/java/com/android/internal/widget/OWNERS @@ -1 +1,7 @@ per-file PointerLocationView.java = michaelwr@google.com, svv@google.com + +# LockSettings related +per-file *LockPattern* = file:/services/core/java/com/android/server/locksettings/OWNERS +per-file *LockScreen* = file:/services/core/java/com/android/server/locksettings/OWNERS +per-file *Lockscreen* = file:/services/core/java/com/android/server/locksettings/OWNERS +per-file *LockSettings* = file:/services/core/java/com/android/server/locksettings/OWNERS diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index a761b4c6af91..e5bc47097751 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -94,6 +94,9 @@ public class SystemConfig { // property for runtime configuration differentiation in vendor private static final String VENDOR_SKU_PROPERTY = "ro.boot.product.vendor.sku"; + // property for background blur support in surface flinger + private static final String BLUR_PROPERTY = "ro.surface_flinger.supports_background_blur"; + // Group-ids that are given to all packages as read from etc/permissions/*.xml. int[] mGlobalGids = EmptyArray.INT; @@ -1242,6 +1245,10 @@ public class SystemConfig { addFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0); } + if (SystemProperties.get(BLUR_PROPERTY, "default").equals("1")) { + addFeature(PackageManager.FEATURE_CROSS_LAYER_BLUR, 0); + } + for (String featureName : mUnavailableFeatures) { removeFeature(featureName); } diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 302ac3cad65d..8dc56ed15d6f 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -1946,7 +1946,7 @@ static jint convertAudioMixToNative(JNIEnv *env, jobject jRule = env->GetObjectField(jAudioMix, gAudioMixFields.mRule); jobject jRuleCriteria = env->GetObjectField(jRule, gAudioMixingRuleFields.mCriteria); - nAudioMix->mAllowPrivilegedPlaybackCapture = + nAudioMix->mAllowPrivilegedMediaPlaybackCapture = env->GetBooleanField(jRule, gAudioMixingRuleFields.mAllowPrivilegedPlaybackCapture); nAudioMix->mVoiceCommunicationCaptureAllowed = env->GetBooleanField(jRule, gAudioMixingRuleFields.mVoiceCommunicationCaptureAllowed); 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..c9579ed8fc22 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). @@ -4070,6 +4087,11 @@ <permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE" android:protectionLevel="signature" /> + <!-- @SystemApi Allows an application to use background blur. + @hide --> + <permission android:name="android.permission.USE_BACKGROUND_BLUR" + android:protectionLevel="signature|privileged" /> + <!-- Allows an application to control the lights on the device. @hide @SystemApi 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.xml b/core/res/res/values/attrs.xml index be7ecfc00f01..9bfd5f5e10f8 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -87,6 +87,16 @@ theme does not set this value, meaning it is based on whether the window is floating. --> <attr name="backgroundDimEnabled" format="boolean" /> + <!-- When windowBackgroundBlurEnabled is set, this is the amount of blur to apply + behind the window. The range is from 0, which means no blur, to 150. + @hide @SystemApi --> + <attr name="windowBackgroundBlurRadius" format="dimension"/> + <!-- If set, the area behind the window will be blurred with radius + windowBackgroundBlurRadius. + @hide @SystemApi --> + <attr name="windowBackgroundBlurEnabled" format="boolean" /> + + <!-- Color of background imagery used for popup windows. --> <attr name="colorPopupBackground" format="color" /> <!-- Color used for list divider. --> @@ -1964,6 +1974,8 @@ <attr name="textColor" /> <attr name="backgroundDimEnabled" /> <attr name="backgroundDimAmount" /> + <attr name="windowBackgroundBlurEnabled" /> + <attr name="windowBackgroundBlurRadius" /> <attr name="windowActionBar" /> <attr name="windowActionModeOverlay" /> <attr name="windowActionBarOverlay" /> @@ -2181,10 +2193,6 @@ the decor view. --> <attr name="windowLightNavigationBar" format="boolean" /> - <!-- @hide --> - <attr name="windowBackgroundBlurRadius" format="dimension"/> - - <!-- Controls how the window is laid out if there is a {@code DisplayCutout}. <p> Defaults to {@code default}. 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/public.xml b/core/res/res/values/public.xml index bac50f1b2c07..ddf3c5f09e9e 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3049,7 +3049,10 @@ <public name="windowLayoutAffinity" /> <public name="canPauseRecording" /> <!-- @hide --> + <!-- @hide @SystemApi --> <public name="windowBackgroundBlurRadius"/> + <!-- @hide @SystemApi --> + <public name="windowBackgroundBlurEnabled"/> <public name="requireDeviceScreenOn" /> <public name="pathSuffix" /> <public name="sspSuffix" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 5715b31b94bc..317a76fa2913 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -110,7 +110,7 @@ <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. --> <string name="ClipMmi">Incoming Caller ID</string> <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. --> - <string name="ClirMmi">Outgoing Caller ID</string> + <string name="ClirMmi">Hide Outgoing Caller ID</string> <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID. --> <string name="ColpMmi">Connected Line ID</string> <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID restriction. --> 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/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/autofill/OWNERS b/core/tests/coretests/src/android/view/autofill/OWNERS new file mode 100644 index 000000000000..9a30e826a24f --- /dev/null +++ b/core/tests/coretests/src/android/view/autofill/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 351486 + +include /core/java/android/view/autofill/OWNERS diff --git a/core/tests/coretests/src/android/view/contentcapture/OWNERS b/core/tests/coretests/src/android/view/contentcapture/OWNERS new file mode 100644 index 000000000000..24561c59bba6 --- /dev/null +++ b/core/tests/coretests/src/android/view/contentcapture/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 544200 + +include /core/java/android/view/contentcapture/OWNERS diff --git a/core/tests/coretests/src/android/view/textclassifier/OWNERS b/core/tests/coretests/src/android/view/textclassifier/OWNERS new file mode 100644 index 000000000000..46b3cb8824a0 --- /dev/null +++ b/core/tests/coretests/src/android/view/textclassifier/OWNERS @@ -0,0 +1 @@ +include /core/java/android/service/textclassifier/OWNERS 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/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java index 6019b90ca13b..597ea14df56d 100644 --- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java +++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java @@ -366,15 +366,6 @@ public class HdmiAudioSystemClientTest { } @Override - public void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled) { - } - - @Override - public boolean isHdmiCecVolumeControlEnabled() { - return true; - } - - @Override public void addHdmiCecVolumeControlFeatureListener( IHdmiCecVolumeControlFeatureListener listener) { } 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/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java b/graphics/java/android/graphics/drawable/BackgroundBlurDrawable.java index 96dac565eb3d..7e75c5beec8b 100644 --- a/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java +++ b/graphics/java/android/graphics/drawable/BackgroundBlurDrawable.java @@ -14,12 +14,13 @@ * limitations under the License. */ -package com.android.internal.graphics.drawable; +package android.graphics.drawable; import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.Context; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; @@ -30,59 +31,71 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RenderNode; -import android.graphics.drawable.Drawable; import android.util.ArrayMap; import android.util.Log; import android.view.SurfaceControl; +import android.view.View; import android.view.ViewRootImpl; -import com.android.internal.R; - /** * A drawable that keeps track of a blur region, pokes a hole under it, and propagates its state * to SurfaceFlinger. + * + * @hide */ +@SystemApi public final class BackgroundBlurDrawable extends Drawable { - private static final String TAG = BackgroundBlurDrawable.class.getSimpleName(); private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private final Aggregator mAggregator; private final RenderNode mRenderNode; private final Paint mPaint = new Paint(); private final Path mRectPath = new Path(); private final float[] mTmpRadii = new float[8]; private final SurfaceControl.BlurRegion mBlurRegion = new SurfaceControl.BlurRegion(); + private Aggregator mAggregator; + // This will be called from a thread pool. private final RenderNode.PositionUpdateListener mPositionUpdateListener = new RenderNode.PositionUpdateListener() { @Override public void positionChanged(long frameNumber, int left, int top, int right, int bottom) { - synchronized (mAggregator) { + if (mAggregator == null) { mBlurRegion.rect.set(left, top, right, bottom); - mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion); + } else { + synchronized (mAggregator) { + mBlurRegion.rect.set(left, top, right, bottom); + mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion); + } } } @Override public void positionLost(long frameNumber) { - synchronized (mAggregator) { + if (mAggregator == null) { mBlurRegion.rect.setEmpty(); - mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion); + } else { + synchronized (mAggregator) { + mBlurRegion.rect.setEmpty(); + mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion); + } } } }; - private BackgroundBlurDrawable(Aggregator aggregator) { - mAggregator = aggregator; + @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR) + public BackgroundBlurDrawable() { mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); mPaint.setColor(Color.TRANSPARENT); mRenderNode = new RenderNode("BackgroundBlurDrawable"); mRenderNode.addPositionUpdateListener(mPositionUpdateListener); } + /** + * @hide + */ @Override public void draw(@NonNull Canvas canvas) { if (mRectPath.isEmpty() || !isVisible() || getAlpha() == 0) { @@ -100,6 +113,9 @@ public final class BackgroundBlurDrawable extends Drawable { mPaint.setColor(color); } + /** + * @hide + */ @Override public boolean setVisible(boolean visible, boolean restart) { boolean changed = super.setVisible(visible, restart); @@ -109,6 +125,9 @@ public final class BackgroundBlurDrawable extends Drawable { return changed; } + /** + * @hide + */ @Override public void setAlpha(int alpha) { mBlurRegion.alpha = alpha / 255f; @@ -139,12 +158,12 @@ public final class BackgroundBlurDrawable extends Drawable { */ public void setCornerRadius(float cornerRadiusTL, float cornerRadiusTR, float cornerRadiusBL, float cornerRadiusBR) { - synchronized (mAggregator) { + maybeRunSynchronized(() -> { mBlurRegion.cornerRadiusTL = cornerRadiusTL; mBlurRegion.cornerRadiusTR = cornerRadiusTR; mBlurRegion.cornerRadiusBL = cornerRadiusBL; mBlurRegion.cornerRadiusBR = cornerRadiusBR; - } + }); updatePath(); invalidateSelf(); } @@ -157,12 +176,13 @@ public final class BackgroundBlurDrawable extends Drawable { } private void updatePath() { - synchronized (mAggregator) { + maybeRunSynchronized(() -> { mTmpRadii[0] = mTmpRadii[1] = mBlurRegion.cornerRadiusTL; mTmpRadii[2] = mTmpRadii[3] = mBlurRegion.cornerRadiusTR; mTmpRadii[4] = mTmpRadii[5] = mBlurRegion.cornerRadiusBL; mTmpRadii[6] = mTmpRadii[7] = mBlurRegion.cornerRadiusBR; - } + }); + mRectPath.reset(); if (getAlpha() == 0 || !isVisible()) { return; @@ -172,19 +192,62 @@ public final class BackgroundBlurDrawable extends Drawable { Path.Direction.CW); } + /** + * @hide + */ @Override public void setColorFilter(@Nullable ColorFilter colorFilter) { throw new IllegalArgumentException("not implemented"); } + /** + * @hide + */ @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } /** + * @hide + */ + @Override + public void onAttached(@NonNull View v) { + super.onAttached(v); + mAggregator = v.getViewRootImpl().getBlurRegionAggregator(); + } + + /** + * @hide + */ + @Override + public void onDetached(@NonNull View v) { + super.onDetached(v); + mAggregator = null; + } + + /** + * The Aggregator is called from the RenderThread to aggregate all blur regions and send them + * to SurfaceFlinger. Since the BackgroundBlurDrawable could be updated at any time from the + * main thread, we need to synchronize the two threads. The BackgroundBlurDrawable may be + * instantiated before the ViewRootImpl is created, i.e. before the Aggregator is created. + * In that case, updates are not synchronized. + */ + private void maybeRunSynchronized(Runnable r) { + if (mAggregator == null) { + r.run(); + } else { + synchronized (mAggregator) { + r.run(); + } + } + } + + /** * Responsible for keeping track of all blur regions of a {@link ViewRootImpl} and posting a * message when it's time to propagate them. + * + * @hide */ public static final class Aggregator { @@ -199,16 +262,6 @@ public final class BackgroundBlurDrawable extends Drawable { } /** - * Creates a blur region with default radius. - */ - public BackgroundBlurDrawable createBackgroundBlurDrawable(Context context) { - BackgroundBlurDrawable drawable = new BackgroundBlurDrawable(this); - drawable.setBlurRadius(context.getResources().getDimensionPixelSize( - R.dimen.default_background_blur_radius)); - return drawable; - } - - /** * Called from RenderThread only, already locked. * @param drawable * @param blurRegion diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index 28b3b04b827d..7f22dc271507 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -1465,6 +1465,24 @@ public abstract class Drawable { } /** + * Notifies the drawable that it has been attached. + * + * @param v The view that it is attached to + * @hide + */ + public void onAttached(View v) { + } + + /** + * Notifies the drawable that it has been detached. + * + * @param v The view that it is detached from + * @hide + */ + public void onDetached(View v) { + } + + /** * Sets the source override density for this Drawable. If non-zero, this density is to be used * for any calls to {@link Resources#getDrawableForDensity(int, int, Theme)} or * {@link Resources#getValueForDensity(int, int, TypedValue, boolean)}. 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 e6fc9da0a9d3..155f6df7b703 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -53,9 +53,8 @@ public: LOG_ALWAYS_FATAL("SkiaCanvas cannot be reset as a recording canvas"); } - virtual uirenderer::DisplayList finishRecording() override { + virtual void finishRecording(uirenderer::RenderNode*) override { LOG_ALWAYS_FATAL("SkiaCanvas does not produce a DisplayList"); - return uirenderer::DisplayList(); } virtual void enableZ(bool enableZ) override { LOG_ALWAYS_FATAL("SkiaCanvas does not support enableZ"); @@ -152,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 9304b7af9cd9..fdfa2883c33f 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -21,7 +21,6 @@ #include <SaveFlags.h> #include <androidfw/ResourceTypes.h> -#include "DisplayList.h" #include "Properties.h" #include "utils/Macros.h" @@ -31,7 +30,7 @@ class SkAnimatedImage; class SkCanvasState; -class SkRuntimeEffect; +class SkRuntimeShaderBuilder; class SkVertices; namespace minikin { @@ -118,7 +117,7 @@ public: virtual void resetRecording(int width, int height, uirenderer::RenderNode* renderNode = nullptr) = 0; - [[nodiscard]] virtual uirenderer::DisplayList finishRecording() = 0; + virtual void finishRecording(uirenderer::RenderNode* destination) = 0; virtual void enableZ(bool enableZ) = 0; bool isHighContrastText() const { return uirenderer::Properties::enableHighContrastText; } @@ -139,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 a74e56183bfd..855d56ee2e55 100644 --- a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp +++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp @@ -102,7 +102,7 @@ static void android_view_DisplayListCanvas_finishRecording( CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jlong renderNodePtr) { Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); - renderNode->setStagingDisplayList(canvas->finishRecording()); + canvas->finishRecording(renderNode); } static void android_view_DisplayListCanvas_drawRenderNode(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jlong renderNodePtr) { @@ -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 5f35155c2b5a..ee7c4d8bb54a 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -55,11 +55,15 @@ void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, in SkiaCanvas::reset(&mRecorder); } -uirenderer::DisplayList SkiaRecordingCanvas::finishRecording() { +std::unique_ptr<SkiaDisplayList> SkiaRecordingCanvas::finishRecording() { // close any existing chunks if necessary enableZ(false); mRecorder.restoreToCount(1); - return uirenderer::DisplayList(std::move(mDisplayList)); + return std::move(mDisplayList); +} + +void SkiaRecordingCanvas::finishRecording(uirenderer::RenderNode* destination) { + destination->setStagingDisplayList(uirenderer::DisplayList(finishRecording())); } // ---------------------------------------------------------------------------- @@ -90,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) { @@ -304,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 ee308f0832a2..8d7a21a732dd 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -39,11 +39,12 @@ public: } virtual void resetRecording(int width, int height, - uirenderer::RenderNode* renderNode) override { + uirenderer::RenderNode* renderNode = nullptr) override { initDisplayList(renderNode, width, height); } - virtual uirenderer::DisplayList finishRecording() override; + virtual void finishRecording(uirenderer::RenderNode* destination) override; + std::unique_ptr<SkiaDisplayList> finishRecording(); virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) override; virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) override; @@ -71,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/libs/hwui/tests/common/TestListViewSceneBase.cpp b/libs/hwui/tests/common/TestListViewSceneBase.cpp index fd331333d38a..43df4a0b1576 100644 --- a/libs/hwui/tests/common/TestListViewSceneBase.cpp +++ b/libs/hwui/tests/common/TestListViewSceneBase.cpp @@ -70,7 +70,7 @@ void TestListViewSceneBase::doFrame(int frameNr) { // draw it to parent DisplayList canvas->drawRenderNode(mListItems[ci].get()); } - mListView->setStagingDisplayList(canvas->finishRecording()); + canvas->finishRecording(mListView.get()); } } // namespace test diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index ba6e8ee290bc..cf8fc82abb4a 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -159,14 +159,6 @@ public: renderthread::RenderThread& renderThread, uint32_t width, uint32_t height, const SkMatrix& transform); - template <class CanvasType> - static std::unique_ptr<DisplayList> createDisplayList( - int width, int height, std::function<void(CanvasType& canvas)> canvasCallback) { - CanvasType canvas(width, height); - canvasCallback(canvas); - return std::unique_ptr<DisplayList>(canvas.finishRecording()); - } - static sp<RenderNode> createNode( int left, int top, int right, int bottom, std::function<void(RenderProperties& props, Canvas& canvas)> setup) { @@ -177,7 +169,7 @@ public: std::unique_ptr<Canvas> canvas( Canvas::create_recording_canvas(props.getWidth(), props.getHeight())); setup(props, *canvas.get()); - node->setStagingDisplayList(canvas->finishRecording()); + canvas->finishRecording(node.get()); } node->setPropertyFieldsDirty(0xFFFFFFFF); return node; @@ -203,7 +195,7 @@ public: std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas( node.stagingProperties().getWidth(), node.stagingProperties().getHeight(), &node)); contentCallback(*canvas.get()); - node.setStagingDisplayList(canvas->finishRecording()); + canvas->finishRecording(&node); } static sp<RenderNode> createSkiaNode( @@ -226,7 +218,7 @@ public: new skiapipeline::SkiaRecordingCanvas(nullptr, props.getWidth(), props.getHeight())); setup(props, *canvas.get()); - node->setStagingDisplayList(canvas->finishRecording()); + canvas->finishRecording(node.get()); } node->setPropertyFieldsDirty(0xFFFFFFFF); TestUtils::syncHierarchyPropertiesAndDisplayList(node); diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp index 0795d13f441b..4271d2f04b88 100644 --- a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp +++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp @@ -55,6 +55,6 @@ public: TestUtils::drawUtf8ToCanvas(canvas.get(), text, paint, 0, 100 * (i + 2)); } - container->setStagingDisplayList(canvas->finishRecording()); + canvas->finishRecording(container.get()); } }; diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp index 1b0a07a98b3f..c6219c485b85 100644 --- a/libs/hwui/tests/common/scenes/TvApp.cpp +++ b/libs/hwui/tests/common/scenes/TvApp.cpp @@ -210,7 +210,7 @@ private: overlay->stagingProperties().getHeight(), overlay.get())); canvas->drawColor((curFrame % 150) << 24, SkBlendMode::kSrcOver); - overlay->setStagingDisplayList(canvas->finishRecording()); + canvas->finishRecording(overlay.get()); cardcanvas->drawRenderNode(overlay.get()); } else { // re-recording image node's canvas, animating ColorFilter @@ -223,11 +223,11 @@ private: paint.setColorFilter(filter); sk_sp<Bitmap> bitmap = mCachedBitmaps[ci]; canvas->drawBitmap(*bitmap, 0, 0, &paint); - image->setStagingDisplayList(canvas->finishRecording()); + canvas->finishRecording(image.get()); cardcanvas->drawRenderNode(image.get()); } - card->setStagingDisplayList(cardcanvas->finishRecording()); + cardcanvas->finishRecording(card.get()); } }; diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp index ade1ddd3f703..9cd10759a834 100644 --- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp +++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp @@ -24,27 +24,28 @@ using namespace android; using namespace android::uirenderer; +using namespace android::uirenderer::skiapipeline; -void BM_DisplayList_alloc(benchmark::State& benchState) { +void BM_SkiaDisplayList_alloc(benchmark::State& benchState) { while (benchState.KeepRunning()) { auto displayList = new skiapipeline::SkiaDisplayList(); benchmark::DoNotOptimize(displayList); delete displayList; } } -BENCHMARK(BM_DisplayList_alloc); +BENCHMARK(BM_SkiaDisplayList_alloc); -void BM_DisplayList_alloc_theoretical(benchmark::State& benchState) { +void BM_SkiaDisplayList_alloc_theoretical(benchmark::State& benchState) { while (benchState.KeepRunning()) { auto displayList = new char[sizeof(skiapipeline::SkiaDisplayList)]; benchmark::DoNotOptimize(displayList); delete[] displayList; } } -BENCHMARK(BM_DisplayList_alloc_theoretical); +BENCHMARK(BM_SkiaDisplayList_alloc_theoretical); -void BM_DisplayListCanvas_record_empty(benchmark::State& benchState) { - std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); +void BM_SkiaDisplayListCanvas_record_empty(benchmark::State& benchState) { + auto canvas = std::make_unique<SkiaRecordingCanvas>(nullptr, 100, 100); static_cast<void>(canvas->finishRecording()); while (benchState.KeepRunning()) { @@ -53,10 +54,10 @@ void BM_DisplayListCanvas_record_empty(benchmark::State& benchState) { static_cast<void>(canvas->finishRecording()); } } -BENCHMARK(BM_DisplayListCanvas_record_empty); +BENCHMARK(BM_SkiaDisplayListCanvas_record_empty); -void BM_DisplayListCanvas_record_saverestore(benchmark::State& benchState) { - std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); +void BM_SkiaDisplayListCanvas_record_saverestore(benchmark::State& benchState) { + auto canvas = std::make_unique<SkiaRecordingCanvas>(nullptr, 100, 100); static_cast<void>(canvas->finishRecording()); while (benchState.KeepRunning()) { @@ -69,10 +70,10 @@ void BM_DisplayListCanvas_record_saverestore(benchmark::State& benchState) { static_cast<void>(canvas->finishRecording()); } } -BENCHMARK(BM_DisplayListCanvas_record_saverestore); +BENCHMARK(BM_SkiaDisplayListCanvas_record_saverestore); -void BM_DisplayListCanvas_record_translate(benchmark::State& benchState) { - std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); +void BM_SkiaDisplayListCanvas_record_translate(benchmark::State& benchState) { + auto canvas = std::make_unique<SkiaRecordingCanvas>(nullptr, 100, 100); static_cast<void>(canvas->finishRecording()); while (benchState.KeepRunning()) { @@ -82,7 +83,7 @@ void BM_DisplayListCanvas_record_translate(benchmark::State& benchState) { static_cast<void>(canvas->finishRecording()); } } -BENCHMARK(BM_DisplayListCanvas_record_translate); +BENCHMARK(BM_SkiaDisplayListCanvas_record_translate); /** * Simulate a simple view drawing a background, overlapped by an image. @@ -90,8 +91,8 @@ BENCHMARK(BM_DisplayListCanvas_record_translate); * Note that the recording commands are intentionally not perfectly efficient, as the * View system frequently produces unneeded save/restores. */ -void BM_DisplayListCanvas_record_simpleBitmapView(benchmark::State& benchState) { - std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); +void BM_SkiaDisplayListCanvas_record_simpleBitmapView(benchmark::State& benchState) { + auto canvas = std::make_unique<SkiaRecordingCanvas>(nullptr, 100, 100); static_cast<void>(canvas->finishRecording()); Paint rectPaint; @@ -114,14 +115,14 @@ void BM_DisplayListCanvas_record_simpleBitmapView(benchmark::State& benchState) static_cast<void>(canvas->finishRecording()); } } -BENCHMARK(BM_DisplayListCanvas_record_simpleBitmapView); +BENCHMARK(BM_SkiaDisplayListCanvas_record_simpleBitmapView); -void BM_DisplayListCanvas_basicViewGroupDraw(benchmark::State& benchState) { +void BM_SkiaDisplayListCanvas_basicViewGroupDraw(benchmark::State& benchState) { sp<RenderNode> child = TestUtils::createNode(50, 50, 100, 100, [](auto& props, auto& canvas) { canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); }); - std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); + auto canvas = std::make_unique<SkiaRecordingCanvas>(nullptr, 100, 100); static_cast<void>(canvas->finishRecording()); while (benchState.KeepRunning()) { @@ -146,4 +147,4 @@ void BM_DisplayListCanvas_basicViewGroupDraw(benchmark::State& benchState) { static_cast<void>(canvas->finishRecording()); } } -BENCHMARK(BM_DisplayListCanvas_basicViewGroupDraw)->Arg(1)->Arg(5)->Arg(10); +BENCHMARK(BM_SkiaDisplayListCanvas_basicViewGroupDraw)->Arg(1)->Arg(5)->Arg(10); diff --git a/libs/hwui/tests/microbench/RenderNodeBench.cpp b/libs/hwui/tests/microbench/RenderNodeBench.cpp index dd3f737abfa9..6aed251481bf 100644 --- a/libs/hwui/tests/microbench/RenderNodeBench.cpp +++ b/libs/hwui/tests/microbench/RenderNodeBench.cpp @@ -35,25 +35,12 @@ BENCHMARK(BM_RenderNode_create); void BM_RenderNode_recordSimple(benchmark::State& state) { sp<RenderNode> node = new RenderNode(); std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); - static_cast<void>(canvas->finishRecording()); + canvas->finishRecording(node.get()); while (state.KeepRunning()) { canvas->resetRecording(100, 100, node.get()); canvas->drawColor(0x00000000, SkBlendMode::kSrcOver); - node->setStagingDisplayList(canvas->finishRecording()); + canvas->finishRecording(node.get()); } } BENCHMARK(BM_RenderNode_recordSimple); - -void BM_RenderNode_recordSimpleWithReuse(benchmark::State& state) { - sp<RenderNode> node = new RenderNode(); - std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); - static_cast<void>(canvas->finishRecording()); - - while (state.KeepRunning()) { - canvas->resetRecording(100, 100, node.get()); - canvas->drawColor(0x00000000, SkBlendMode::kSrcOver); - canvas->finishRecording().clear(node.get()); - } -} -BENCHMARK(BM_RenderNode_recordSimpleWithReuse);
\ No newline at end of file diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp index 801a294b5648..3d5aca4bf05a 100644 --- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp +++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp @@ -38,13 +38,12 @@ TEST(SkiaDisplayList, create) { } TEST(SkiaDisplayList, reset) { - DisplayList displayList; + std::unique_ptr<SkiaDisplayList> skiaDL; { SkiaRecordingCanvas canvas{nullptr, 1, 1}; canvas.drawColor(0, SkBlendMode::kSrc); - displayList = canvas.finishRecording(); + skiaDL = canvas.finishRecording(); } - SkiaDisplayList* skiaDL = displayList.asSkiaDl(); SkCanvas dummyCanvas; RenderNodeDrawable drawable(nullptr, &dummyCanvas); diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index 13bd85634fda..2703ee35b099 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -1993,6 +1993,33 @@ public final class MediaDrm implements AutoCloseable { return signRSANative(this, sessionId, algorithm, wrappedKey, message); } + /** + * Query if the crypto scheme requires the use of a secure decoder + * to decode data of the given mime type at the default security level. + * The default security level is defined as the highest security level + * supported on the device. + * + * @param mime The mime type of the media data + */ + public boolean requiresSecureDecoder(@NonNull String mime) { + return requiresSecureDecoder(mime, getMaxSecurityLevel()); + } + + /** + * Query if the crypto scheme requires the use of a secure decoder + * to decode data of the given mime type at the given security level. + * + * @param mime The mime type of the media data + * @param level a security level between {@link #SECURITY_LEVEL_SW_SECURE_CRYPTO} + * and {@link #SECURITY_LEVEL_HW_SECURE_ALL}. Otherwise the special value + * {@link #getMaxSecurityLevel()} is also permitted; + * use {@link #getMaxSecurityLevel()} to indicate the maximum security level + * supported by the device. + * @throws IllegalArgumentException if the requested security level is none of the documented + * values for the parameter {@code level}. + */ + public native boolean requiresSecureDecoder(@NonNull String mime, @SecurityLevel int level); + @Override protected void finalize() throws Throwable { try { diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java index 221147dcad8f..0c733482b2b5 100644 --- a/media/java/android/media/audiopolicy/AudioMix.java +++ b/media/java/android/media/audiopolicy/AudioMix.java @@ -218,7 +218,7 @@ public class AudioMix { /** @return an error string if the format would not allow Privileged playbackCapture * null otherwise * @hide */ - public static String canBeUsedForPrivilegedCapture(AudioFormat format) { + public static String canBeUsedForPrivilegedMediaCapture(AudioFormat format) { int sampleRate = format.getSampleRate(); if (sampleRate > PRIVILEDGED_CAPTURE_MAX_SAMPLE_RATE || sampleRate <= 0) { return "Privileged audio capture sample rate " + sampleRate @@ -448,8 +448,8 @@ public class AudioMix { } } } - if (mRule.allowPrivilegedPlaybackCapture()) { - String error = AudioMix.canBeUsedForPrivilegedCapture(mFormat); + if (mRule.allowPrivilegedMediaPlaybackCapture()) { + String error = AudioMix.canBeUsedForPrivilegedMediaCapture(mFormat); if (error != null) { throw new IllegalArgumentException(error); } diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java index de153135eeb9..1f07705a4d94 100644 --- a/media/java/android/media/audiopolicy/AudioMixingRule.java +++ b/media/java/android/media/audiopolicy/AudioMixingRule.java @@ -46,11 +46,11 @@ import java.util.Objects; public class AudioMixingRule { private AudioMixingRule(int mixType, ArrayList<AudioMixMatchCriterion> criteria, - boolean allowPrivilegedPlaybackCapture, + boolean allowPrivilegedMediaPlaybackCapture, boolean voiceCommunicationCaptureAllowed) { mCriteria = criteria; mTargetMixType = mixType; - mAllowPrivilegedPlaybackCapture = allowPrivilegedPlaybackCapture; + mAllowPrivilegedPlaybackCapture = allowPrivilegedMediaPlaybackCapture; mVoiceCommunicationCaptureAllowed = voiceCommunicationCaptureAllowed; } @@ -204,13 +204,17 @@ public class AudioMixingRule { private final ArrayList<AudioMixMatchCriterion> mCriteria; /** @hide */ public ArrayList<AudioMixMatchCriterion> getCriteria() { return mCriteria; } + /** Indicates that this rule is intended to capture media or game playback by a system component + * with permission CAPTURE_MEDIA_OUTPUT or CAPTURE_AUDIO_OUTPUT. + */ + //TODO b/177061175: rename to mAllowPrivilegedMediaPlaybackCapture @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private boolean mAllowPrivilegedPlaybackCapture = false; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private boolean mVoiceCommunicationCaptureAllowed = false; /** @hide */ - public boolean allowPrivilegedPlaybackCapture() { + public boolean allowPrivilegedMediaPlaybackCapture() { return mAllowPrivilegedPlaybackCapture; } @@ -311,7 +315,7 @@ public class AudioMixingRule { public static class Builder { private ArrayList<AudioMixMatchCriterion> mCriteria; private int mTargetMixType = AudioMix.MIX_TYPE_INVALID; - private boolean mAllowPrivilegedPlaybackCapture = false; + private boolean mAllowPrivilegedMediaPlaybackCapture = false; // This value should be set internally according to a permission check private boolean mVoiceCommunicationCaptureAllowed = false; @@ -434,7 +438,7 @@ public class AudioMixingRule { * @return the same Builder instance. */ public @NonNull Builder allowPrivilegedPlaybackCapture(boolean allow) { - mAllowPrivilegedPlaybackCapture = allow; + mAllowPrivilegedMediaPlaybackCapture = allow; return this; } @@ -639,7 +643,7 @@ public class AudioMixingRule { */ public AudioMixingRule build() { return new AudioMixingRule(mTargetMixType, mCriteria, - mAllowPrivilegedPlaybackCapture, mVoiceCommunicationCaptureAllowed); + mAllowPrivilegedMediaPlaybackCapture, mVoiceCommunicationCaptureAllowed); } } } diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java index 697d80c6b78e..ede68bd5022b 100644 --- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java +++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java @@ -97,7 +97,7 @@ public class AudioPolicyConfig implements Parcelable { dest.writeInt(mix.getFormat().getEncoding()); dest.writeInt(mix.getFormat().getChannelMask()); // write opt-out respect - dest.writeBoolean(mix.getRule().allowPrivilegedPlaybackCapture()); + dest.writeBoolean(mix.getRule().allowPrivilegedMediaPlaybackCapture()); // write voice communication capture allowed flag dest.writeBoolean(mix.getRule().voiceCommunicationCaptureAllowed()); // write mix rules @@ -172,7 +172,7 @@ public class AudioPolicyConfig implements Parcelable { textDump += " channels=0x"; textDump += Integer.toHexString(mix.getFormat().getChannelMask()).toUpperCase() + "\n"; textDump += " ignore playback capture opt out=" - + mix.getRule().allowPrivilegedPlaybackCapture() + "\n"; + + mix.getRule().allowPrivilegedMediaPlaybackCapture() + "\n"; textDump += " allow voice communication capture=" + mix.getRule().voiceCommunicationCaptureAllowed() + "\n"; // write mix rules diff --git a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl index 85c93612e7e2..e55678d90078 100644 --- a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl +++ b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl @@ -16,8 +16,11 @@ 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. @@ -26,5 +29,8 @@ import android.media.metrics.PlaybackMetrics; interface IPlaybackMetricsManager { void reportPlaybackMetrics(in String sessionId, in PlaybackMetrics metrics, int userId); String getSessionId(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/NetworkEvent.aidl b/media/java/android/media/metrics/NetworkEvent.aidl new file mode 100644 index 000000000000..2b7fa02aae64 --- /dev/null +++ b/media/java/android/media/metrics/NetworkEvent.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 NetworkEvent; diff --git a/media/java/android/media/metrics/NetworkEvent.java b/media/java/android/media/metrics/NetworkEvent.java new file mode 100644 index 000000000000..a330bc0b66df --- /dev/null +++ b/media/java/android/media/metrics/NetworkEvent.java @@ -0,0 +1,203 @@ +/* + * 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 network event. + * @hide + */ +public final class NetworkEvent implements Parcelable { + public static final int NETWORK_TYPE_NONE = 0; + public static final int NETWORK_TYPE_OTHER = 1; + public static final int NETWORK_TYPE_WIFI = 2; + public static final int NETWORK_TYPE_ETHERNET = 3; + public static final int NETWORK_TYPE_2G = 4; + public static final int NETWORK_TYPE_3G = 5; + public static final int NETWORK_TYPE_4G = 6; + public static final int NETWORK_TYPE_5G_NSA = 7; + public static final int NETWORK_TYPE_5G_SA = 8; + + private final int mType; + private final long mTimeSincePlaybackCreatedMillis; + + /** @hide */ + @IntDef(prefix = "NETWORK_TYPE_", value = { + NETWORK_TYPE_NONE, + NETWORK_TYPE_OTHER, + NETWORK_TYPE_WIFI, + NETWORK_TYPE_ETHERNET, + NETWORK_TYPE_2G, + NETWORK_TYPE_3G, + NETWORK_TYPE_4G, + NETWORK_TYPE_5G_NSA, + NETWORK_TYPE_5G_SA + }) + @Retention(RetentionPolicy.SOURCE) + public @interface NetworkType {} + + /** + * Network type to string. + */ + public static String networkTypeToString(@NetworkType int value) { + switch (value) { + case NETWORK_TYPE_NONE: + return "NETWORK_TYPE_NONE"; + case NETWORK_TYPE_OTHER: + return "NETWORK_TYPE_OTHER"; + case NETWORK_TYPE_WIFI: + return "NETWORK_TYPE_WIFI"; + case NETWORK_TYPE_ETHERNET: + return "NETWORK_TYPE_ETHERNET"; + case NETWORK_TYPE_2G: + return "NETWORK_TYPE_2G"; + case NETWORK_TYPE_3G: + return "NETWORK_TYPE_3G"; + case NETWORK_TYPE_4G: + return "NETWORK_TYPE_4G"; + case NETWORK_TYPE_5G_NSA: + return "NETWORK_TYPE_5G_NSA"; + case NETWORK_TYPE_5G_SA: + return "NETWORK_TYPE_5G_SA"; + default: + return Integer.toHexString(value); + } + } + + /** + * Creates a new NetworkEvent. + * + * @hide + */ + public NetworkEvent(@NetworkType int type, long timeSincePlaybackCreatedMillis) { + this.mType = type; + this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis; + } + + @NetworkType + public int getType() { + return mType; + } + + public long getTimeSincePlaybackCreatedMillis() { + return mTimeSincePlaybackCreatedMillis; + } + + @Override + public String toString() { + return "NetworkEvent { " + + "type = " + mType + ", " + + "timeSincePlaybackCreatedMillis = " + mTimeSincePlaybackCreatedMillis + + " }"; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NetworkEvent that = (NetworkEvent) o; + return mType == that.mType + && mTimeSincePlaybackCreatedMillis == that.mTimeSincePlaybackCreatedMillis; + } + + @Override + public int hashCode() { + return Objects.hash(mType, mTimeSincePlaybackCreatedMillis); + } + + @Override + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + dest.writeInt(mType); + dest.writeLong(mTimeSincePlaybackCreatedMillis); + } + + @Override + public int describeContents() { + return 0; + } + + /** @hide */ + /* package-private */ NetworkEvent(@NonNull android.os.Parcel in) { + int type = in.readInt(); + long timeSincePlaybackCreatedMillis = in.readLong(); + + this.mType = type; + this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis; + } + + public static final @NonNull Parcelable.Creator<NetworkEvent> CREATOR = + new Parcelable.Creator<NetworkEvent>() { + @Override + public NetworkEvent[] newArray(int size) { + return new NetworkEvent[size]; + } + + @Override + public NetworkEvent createFromParcel(@NonNull Parcel in) { + return new NetworkEvent(in); + } + }; + + /** + * A builder for {@link NetworkEvent} + */ + public static final class Builder { + private int mType; + private long mTimeSincePlaybackCreatedMillis; + + /** + * Creates a new Builder. + * + * @hide + */ + public Builder() { + } + + /** + * Sets network type. + */ + public @NonNull Builder setType(@NetworkType int value) { + mType = value; + return this; + } + + /** + * Sets timestamp since the creation in milliseconds. + */ + public @NonNull Builder setTimeSincePlaybackCreatedMillis(long value) { + mTimeSincePlaybackCreatedMillis = value; + return this; + } + + /** Builds the instance. */ + public @NonNull NetworkEvent build() { + NetworkEvent o = new NetworkEvent( + mType, + mTimeSincePlaybackCreatedMillis); + return o; + } + } +} 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 b778bbd6fa1d..f48ffe7f3b22 100644 --- a/media/java/android/media/metrics/PlaybackMetricsManager.java +++ b/media/java/android/media/metrics/PlaybackMetricsManager.java @@ -48,6 +48,41 @@ public class PlaybackMetricsManager { throw e.rethrowFromSystemServer(); } } + /** + * Reports network event. + * @hide + */ + public void reportNetworkEvent(@NonNull String sessionId, NetworkEvent event) { + try { + mService.reportNetworkEvent(sessionId, event, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * 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. diff --git a/media/java/android/media/metrics/PlaybackSession.java b/media/java/android/media/metrics/PlaybackSession.java index 07f4fbc219c7..0a77516a0b8c 100644 --- a/media/java/android/media/metrics/PlaybackSession.java +++ b/media/java/android/media/metrics/PlaybackSession.java @@ -57,6 +57,27 @@ public final class PlaybackSession implements AutoCloseable { mManager.reportPlaybackErrorEvent(mId, event); } + /** + * Reports network event. + */ + public void reportNetworkEvent(NetworkEvent event) { + 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_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp index f38a29c69a3e..babb16b1c880 100644 --- a/media/jni/android_media_MediaDrm.cpp +++ b/media/jni/android_media_MediaDrm.cpp @@ -1953,6 +1953,30 @@ static jbyteArray android_media_MediaDrm_signRSANative( return VectorToJByteArray(env, signature); } +static jboolean android_media_MediaDrm_requiresSecureDecoder( + JNIEnv *env, jobject thiz, jstring jmimeType, + jint jSecurityLevel) { + sp<IDrm> drm = GetDrm(env, thiz); + if (!CheckDrm(env, drm)) { + return JNI_FALSE; + } + + String8 mimeType; + if (jmimeType != NULL) { + mimeType = JStringToString8(env, jmimeType); + } + + DrmPlugin::SecurityLevel securityLevel = jintToSecurityLevel(jSecurityLevel); + if (securityLevel == DrmPlugin::kSecurityLevelUnknown) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid security level"); + return JNI_FALSE; + } + + if (securityLevel == DrmPlugin::kSecurityLevelMax) { + return drm->requiresSecureDecoder(mimeType.c_str()); + } + return drm->requiresSecureDecoder(mimeType.c_str(), securityLevel); +} static const JNINativeMethod gMethods[] = { { "native_release", "()V", (void *)android_media_MediaDrm_native_release }, @@ -2075,6 +2099,9 @@ static const JNINativeMethod gMethods[] = { { "getMetricsNative", "()Landroid/os/PersistableBundle;", (void *)android_media_MediaDrm_native_getMetrics }, + + { "requiresSecureDecoder", "(Ljava/lang/String;I)Z", + (void *)android_media_MediaDrm_requiresSecureDecoder }, }; int register_android_media_Drm(JNIEnv *env) { 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/android/include_platform/android/activity_manager.h b/native/android/include_platform/android/activity_manager.h index 0ecd56d512a2..aa86c74e16c9 100644 --- a/native/android/include_platform/android/activity_manager.h +++ b/native/android/include_platform/android/activity_manager.h @@ -115,8 +115,6 @@ enum { AACTIVITYMANAGER_IMPORTANCE_GONE = 1000, }; -#if __ANDROID_API__ >= 31 - /** * Adds a UidImportanceListener to the ActivityManager. * @@ -169,8 +167,6 @@ bool AActivityManager_isUidActive(uid_t uid) __INTRODUCED_IN(31); */ int32_t AActivityManager_getUidImportance(uid_t uid) __INTRODUCED_IN(31); -#endif // __ANDROID_API__ >= 31 - __END_DECLS #endif // __AACTIVITYMANAGER_H__ diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp index 4120a7326817..3751564ca465 100644 --- a/native/graphics/jni/Android.bp +++ b/native/graphics/jni/Android.bp @@ -22,22 +22,20 @@ cc_library_shared { "-Wunreachable-code", ], - // our source files - // srcs: [ - "aassetstreamadaptor.cpp", - "bitmap.cpp", "imagedecoder.cpp", ], shared_libs: [ - "libandroid", "libandroid_runtime", "libhwui", "liblog", ], - header_libs: [ "libhwui_internal_headers" ], + header_libs: [ + "libhwui_internal_headers", + "jni_headers", + ], static_libs: ["libarect"], @@ -48,7 +46,24 @@ cc_library_shared { ldflags: ["-Wl,--hash-style=both"], }, }, - version_script: "libjnigraphics.map.txt", + host_supported: true, + target: { + android: { + srcs: [ + "aassetstreamadaptor.cpp", + "bitmap.cpp", + ], + shared_libs: [ + "libandroid", + ], + version_script: "libjnigraphics.map.txt", + }, + host: { + header_libs: [ + "libnativewindow_headers", + ], + }, + }, } // The headers module is in frameworks/native/Android.bp. @@ -71,6 +86,17 @@ cc_fuzz { static_libs: ["libarect"], fuzz_config: { cc: ["scroggo@google.com"], + 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/corpus/webp-color-profile-lossless.webp b/native/graphics/jni/corpus/webp-color-profile-lossless.webp Binary files differnew file mode 100644 index 000000000000..4fd63d5794ff --- /dev/null +++ b/native/graphics/jni/corpus/webp-color-profile-lossless.webp diff --git a/native/graphics/jni/fuzz_imagedecoder.cpp b/native/graphics/jni/fuzz_imagedecoder.cpp index f2cd1a8f4eb9..015aca70e4e0 100644 --- a/native/graphics/jni/fuzz_imagedecoder.cpp +++ b/native/graphics/jni/fuzz_imagedecoder.cpp @@ -73,6 +73,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { return 0; } - AImageDecoder_decodeImage(decoder.get(), pixels.get(), stride, pixelSize); + while (true) { + int result = AImageDecoder_decodeImage(decoder.get(), pixels.get(), stride, pixelSize); + if (result != ANDROID_IMAGE_DECODER_SUCCESS) break; + + result = AImageDecoder_advanceFrame(decoder.get()); + if (result != ANDROID_IMAGE_DECODER_SUCCESS) break; + } return 0; } diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp index 5973790e48fd..385e455e3e1f 100644 --- a/native/graphics/jni/imagedecoder.cpp +++ b/native/graphics/jni/imagedecoder.cpp @@ -121,8 +121,12 @@ int AImageDecoder_createFromAAsset(AAsset* asset, AImageDecoder** outDecoder) { } *outDecoder = nullptr; +#ifdef __ANDROID__ auto stream = std::make_unique<AAssetStreamAdaptor>(asset); return createFromStream(std::move(stream), outDecoder); +#else + return ANDROID_IMAGE_DECODER_INTERNAL_ERROR; +#endif } static bool isSeekable(int descriptor) { @@ -349,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/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java index fc93650a6b85..723caf2ce0a1 100644 --- a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java +++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java @@ -45,12 +45,15 @@ public class AdaptiveIcon extends LayerDrawable { private AdaptiveConstantState mAdaptiveConstantState; public AdaptiveIcon(Context context, Drawable foreground) { + this(context, foreground, R.dimen.dashboard_tile_foreground_image_inset); + } + + public AdaptiveIcon(Context context, Drawable foreground, int insetResId) { super(new Drawable[]{ new AdaptiveIconShapeDrawable(context.getResources()), foreground }); - final int insetPx = context.getResources() - .getDimensionPixelSize(R.dimen.dashboard_tile_foreground_image_inset); + final int insetPx = context.getResources().getDimensionPixelSize(insetResId); setLayerInset(1 /* index */, insetPx, insetPx, insetPx, insetPx); mAdaptiveConstantState = new AdaptiveConstantState(context, foreground); } diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml index b1553e9f463f..7e3ce9d3595c 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml @@ -35,6 +35,7 @@ android:layout_height="wrap_content" android:layout_width="0dp" android:layout_weight="1" + android:paddingRight="54dp" android:layout_gravity="center_vertical" android:maxLines="2" android:ellipsize="end" diff --git a/packages/SettingsLib/SettingsSpinner/Android.bp b/packages/SettingsLib/SettingsSpinner/Android.bp index f18917cd2ebb..ca23616ca588 100644 --- a/packages/SettingsLib/SettingsSpinner/Android.bp +++ b/packages/SettingsLib/SettingsSpinner/Android.bp @@ -4,6 +4,10 @@ android_library { srcs: ["src/**/*.java"], resource_dirs: ["res"], + static_libs: [ + "androidx.preference_preference", + ], + sdk_version: "system_current", min_sdk_version: "21", } diff --git a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml b/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml new file mode 100644 index 000000000000..7d5b6db6f6d6 --- /dev/null +++ b/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="?android:attr/listPreferredItemHeight" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp"> + + <com.android.settingslib.widget.settingsspinner.SettingsSpinner + android:id="@+id/spinner" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true"/> +</RelativeLayout> + diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java new file mode 100644 index 000000000000..154a0f44788d --- /dev/null +++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.AdapterView; + +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + +import com.android.settingslib.widget.settingsspinner.SettingsSpinner; +import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter; + +/** + * This preference uses SettingsSpinner & SettingsSpinnerAdapter which provide default layouts for + * both view and drop down view of the Spinner. + */ +public class SettingsSpinnerPreference extends Preference { + + private SettingsSpinnerAdapter mAdapter; + private AdapterView.OnItemSelectedListener mListener; + private int mPosition; //Default 0 for internal shard storage. + + /** + * Perform inflation from XML and apply a class-specific base style. + * + * @param context The {@link Context} this is associated with, through which it can + * access the current theme, resources, {@link SharedPreferences}, etc. + * @param attrs The attributes of the XML tag that is inflating the preference + * @param defStyle An attribute in the current theme that contains a reference to a style + * resource that supplies default values for the view. Can be 0 to not + * look for defaults. + */ + public SettingsSpinnerPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setLayoutResource(R.layout.settings_spinner_preference); + } + + /** + * Perform inflation from XML and apply a class-specific base style. + * + * @param context The {@link Context} this is associated with, through which it can + * access the current theme, resources, {@link SharedPreferences}, etc. + * @param attrs The attributes of the XML tag that is inflating the preference + */ + public SettingsSpinnerPreference(Context context, AttributeSet attrs) { + super(context, attrs); + setLayoutResource(R.layout.settings_spinner_preference); + } + + /** + * Constructor to create a preference. + * + * @param context The Context this is associated with. + */ + public SettingsSpinnerPreference(Context context) { + this(context, null); + } + + /** Sets adapter of the spinner. */ + public <T extends SettingsSpinnerAdapter> void setAdapter(T adapter) { + mAdapter = adapter; + notifyChanged(); + } + + /** Sets item selection listener of the spinner. */ + public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) { + mListener = listener; + } + + /** Gets selected item of the spinner. */ + public Object getSelectedItem() { + return mAdapter == null ? null : mAdapter.getItem(mPosition); + } + + /** Gets selection position of the spinner */ + public void setSelection(int position) { + if (mPosition == position) { + return; + } + mPosition = position; + notifyChanged(); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + final SettingsSpinner spinner = (SettingsSpinner) holder.findViewById(R.id.spinner); + spinner.setAdapter(mAdapter); + spinner.setSelection(mPosition); + spinner.setOnItemSelectedListener(mOnSelectedListener); + } + + private final AdapterView.OnItemSelectedListener mOnSelectedListener = + new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + if (mPosition == position) return; + mPosition = position; + if (mListener != null) { + mListener.onItemSelected(parent, view, position, id); + } + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + if (mListener != null) { + mListener.onNothingSelected(parent); + } + } + }; +} diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java index f9aedd9c07a4..a8ca0d8664f3 100644 --- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java +++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java @@ -17,6 +17,9 @@ package com.android.settingslib.widget.settingsspinner; import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; import android.widget.ArrayAdapter; import com.android.settingslib.widget.R; @@ -26,6 +29,11 @@ import com.android.settingslib.widget.R; */ public class SettingsSpinnerAdapter<T> extends ArrayAdapter<T> { + private static final int DEFAULT_RESOURCE = R.layout.settings_spinner_view; + private static final int DFAULT_DROPDOWN_RESOURCE = + android.R.layout.simple_spinner_dropdown_item; + private final LayoutInflater mDefaultInflater; + /** * Constructs a new SettingsSpinnerAdapter with the given context. * And it customizes title bar with a settings style. @@ -34,7 +42,24 @@ public class SettingsSpinnerAdapter<T> extends ArrayAdapter<T> { * access the current theme, resources, etc. */ public SettingsSpinnerAdapter(Context context) { - super(context, R.layout.settings_spinner_view); - setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + super(context, DEFAULT_RESOURCE); + + setDropDownViewResource(DFAULT_DROPDOWN_RESOURCE); + mDefaultInflater = LayoutInflater.from(context); + } + + /** + * In overridded {@link #getView(int, View, ViewGroup)}, use this method to get default view. + */ + public View getDefaultView(int position, View convertView, ViewGroup parent) { + return mDefaultInflater.inflate(DEFAULT_RESOURCE, parent, false /* attachToRoot */); + } + + /** + * In overridded {@link #getDropDownView(int, View, ViewGroup)}, use this method to get default + * drop down view. + */ + public View getDefaultDropDownView(int position, View convertView, ViewGroup parent) { + return mDefaultInflater.inflate(DFAULT_DROPDOWN_RESOURCE, parent, false /* attachToRoot */); } } 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-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index defbf54ccb10..6f4f72aae8ca 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/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 (diensverskaffer-wi-fi)"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobiele data is af"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Nie gestel om data te gebruik nie"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Geen foon nie."</string> 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-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 1326c5190a79..3f5df34b07b0 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/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-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-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 702a41948eb2..0351d946c05c 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -580,8 +580,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"شبكة الجيل الرابع أو أحدث"</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-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-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index c5dbda907cef..0f7db8f1e966 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -576,8 +576,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string> <string name="data_connection_lte" msgid="7675461204366364124">"এলটিই"</string> <string name="data_connection_lte_plus" msgid="6643158654804916653">"এলটিই+"</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-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-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index b18b1c416588..0855d17e8c70 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/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">"Mobil data deaktivdir"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Data istifadə etmək üçün ayarlanmayıb"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Telefon yoxdur."</string> 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-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index d92f171bd049..3386fe8649ea 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -577,8 +577,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">"WiFi mobilnog operatera"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobilni podaci su isključeni"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Nije podešeno za korišćenje podataka"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Nema telefona."</string> 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-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 57d9a554fef0..6ac2172496c4 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/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">"Мабільная перадача даных выключана"</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-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-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 4d4b392f1feb..19ed5bdd5688 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/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-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-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 88fb83f3b295..b0e9342c19c6 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/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-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-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 6925d044f6c9..9377624d6d26 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -577,8 +577,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">"Prijenos podataka na mobilnoj mreži je isključen"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Nije postavljeno za korištenje podataka"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Nema telefonskog signala."</string> 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-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index dd3a8118510a..a2a7770e7db7 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/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">"S\'han desactivat les dades mòbils"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"No s\'ha definit per utilitzar dades"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"No hi ha senyal de telèfon."</string> 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-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 6b8914d457dc..4db4d5c81e3f 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/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">"Wi-Fi operátora"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobilní data jsou vypnuta"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Nenastaveno k využití dat"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Žádná telefonní síť."</string> 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-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index c8b4e2b5d42c..b76afa5c42d6 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/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">"Mobildata er deaktiveret"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Ikke indstillet til at anvende data"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Ingen telefon."</string> 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-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index eb87a585cfdd..c608a629a80d 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/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-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-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index ad16c89d3871..e67c3d1ddc5a 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/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 data off"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Not set to use data"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"No phone."</string> 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-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index 451590847147..b0830fc40e2e 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/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 data off"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Not set to use data"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"No phone."</string> 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-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index ad16c89d3871..e67c3d1ddc5a 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/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 data off"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Not set to use data"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"No phone."</string> 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-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index ad16c89d3871..e67c3d1ddc5a 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/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 data off"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Not set to use data"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"No phone."</string> 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-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index f8903bb23877..4c0af75fada6 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/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 data off"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Not set to use data"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"No phone."</string> 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-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 26cffd20a0e7..9ae77819029b 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/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">"Datos móviles desactivados"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"No se configuró para usar datos"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Sin teléfono"</string> 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-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 5a58a20fb63b..203726a5d557 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/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">"Datos desactiv."</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"No está establecido para usar los datos"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Sin teléfono"</string> 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-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 2d5d0b5f4eb3..379ed6cb9065 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/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">"Mobiilne andmeside on väljas"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Ei ole andmeside kasutamiseks seadistatud"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Telefonisignaal puudub"</string> 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-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index ae99c010ec39..cb598e523100 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/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-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-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 6dd3a21db0fd..31dfe1829133 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/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">"Operaattorin Wi-Fi"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobiilidata poistettu käytöstä"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Ei käytä dataa"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Ei puhelinverkkoyhteyttä."</string> 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-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index cf8ec408106b..46bd71b9b382 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/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-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-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index ddcfc5804ba5..9cae3112cd48 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -576,8 +576,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string> <string name="data_connection_lte" msgid="7675461204366364124">"एलटीई"</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-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-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index f7d2bec35406..83bb2d1e7732 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -577,8 +577,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G i više"</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">"Mobilni su podaci isključeni"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Nije postavljeno za upotrebu podataka"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Nema telefona."</string> 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-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 883e2a42ea93..9f184c7052f5 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/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">"Szolgáltatói Wi-Fi"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobiladatok kikapcsolva"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Nincs beállítva az adathasználat"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Nincs telefon."</string> 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-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index de9e4dd87dcf..cd6cbf3bc396 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/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-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-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index e4f56e6e45da..3b8091872562 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/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">"Kuota nonaktif"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Tidak disetel untuk menggunakan data"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Tidak dapat melakukan panggilan."</string> 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-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index d5d828928097..ce9e66522aaf 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/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">"Wi-Fi símafyrirtækis (CWF)"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Slökkt á farsímagögnum"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Ekki stillt á að nota gögn"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Ekkert símasamband."</string> 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-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 55c03b6fbe08..93b24a0bf066 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/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">"Wi-Fi operatore"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Dati mobili disattivati"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Non impostato per l\'utilizzo dei dati"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Nessun telefono."</string> 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-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 6bbfa16d6597..ab678092dd9c 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/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">"Wi-Fi של הספק"</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-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-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 3105e98c3f90..711a22f8a41e 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/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">"モバイルデータ OFF"</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-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-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index d97926b8ae76..08133056c408 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/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-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-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 6cceef600759..16ee9c00fa18 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -359,7 +359,7 @@ <string name="enable_gpu_debug_layers" msgid="4986675516188740397">"GPU жөндеу қабаттарын қосу"</string> <string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"GPU жөндеу қабаттарының жүктелуіне рұқсат ету"</string> <string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Жеткізушілерді журналға тіркеу"</string> - <string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Қате туралы есепте қызмет көрсетушінің құрылғыға қатысты қосымша ақпаратын қамту. Мұнда жеке ақпарат көрсетілуі, батарея шығыны артуы және/немесе қосымша жад пайдаланылуы мүмкін."</string> + <string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Қате туралы есепте жеткізушінің құрылғыға қатысты қосымша ақпараты қамтылады. Мұнда жеке ақпарат көрсетілуі, батарея шығыны артуы және/немесе қосымша жад пайдаланылуы мүмкін."</string> <string name="window_animation_scale_title" msgid="5236381298376812508">"Терезе анимациясының өлшемі"</string> <string name="transition_animation_scale_title" msgid="1278477690695439337">"Ауысу анимациясының өлшемі"</string> <string name="animator_duration_scale_title" msgid="7082913931326085176">"Аниматор ұзақтығы"</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">"Мобильдік деректер өшірулі"</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-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-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index f069d1530c0c..5b47381758f7 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/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-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-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 3841e3bec853..fd63dcb343ae 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/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-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-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 75b731b18fdd..a9c3f316f4c2 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/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-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-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 115937677c2d..bc3656f0846f 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/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-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-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 3e3f3176d423..8408c93459a6 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/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-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-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 279980be8c77..1dcfcf7bbb36 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/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">"Mobiliojo ryšio duomenys išjungti"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Nenustatyta naudoti duomenis"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Nėra telefono."</string> 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-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index f36adc6b7bb1..3d9b78acf6ed 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -577,8 +577,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">"Mobilie dati izslēgti"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Nav iestatīts datu lietošanai"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Nav tālruņa."</string> 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-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 31e5b7c9f275..0674f118c04b 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/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">"Wi-Fi на операторот"</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-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-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 6ce0a7dee163..f13cb0b03884 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/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-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-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index c1a246f2dd74..72ddc8f9b024 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -168,7 +168,7 @@ <string name="tts_play_example_summary" msgid="634044730710636383">"उच्चार संश्लेषणाचे एक छोटेसे प्रात्यक्षिक प्ले करा"</string> <string name="tts_install_data_title" msgid="1829942496472751703">"व्हॉइस डेटा इंस्टॉल करा"</string> <string name="tts_install_data_summary" msgid="3608874324992243851">"उच्चार संश्लेषणासाठी आवश्यक आवाज डेटा इंस्टॉल करा"</string> - <string name="tts_engine_security_warning" msgid="3372432853837988146">"हे उच्चार संश्लेषण इंजिन पासवर्ड आणि क्रेडिट कार्ड नंबर यासारख्या वैयक्तिक मजकुरासह, बोलला जाणारा सर्व मजकूर संकलित करण्यात सक्षम होऊ शकते. हे <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> इंजिनवरून येते. या उच्चार संश्लेषण इंजिनचा वापर सक्षम करायचा?"</string> + <string name="tts_engine_security_warning" msgid="3372432853837988146">"हे उच्चार संश्लेषण इंजीन पासवर्ड आणि क्रेडिट कार्ड नंबर यासारख्या वैयक्तिक मजकुरासह, बोलला जाणारा सर्व मजकूर संकलित करण्यात सक्षम होऊ शकते. हे <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> इंजीनवरून येते. या उच्चार संश्लेषण इंजीनचा वापर सक्षम करायचा?"</string> <string name="tts_engine_network_required" msgid="8722087649733906851">"या भाषेस टेक्स्ट टू स्पीचसाठी एका नेटवर्क कनेक्शनची आवश्यकता आहे."</string> <string name="tts_default_sample_string" msgid="6388016028292967973">"हे उच्चार संश्लेषणाचे एक उदाहरण आहे"</string> <string name="tts_status_title" msgid="8190784181389278640">"डीफॉल्ट भाषा स्थिती"</string> @@ -177,8 +177,8 @@ <string name="tts_status_not_supported" msgid="2702997696245523743">"<xliff:g id="LOCALE">%1$s</xliff:g> समर्थित नाही"</string> <string name="tts_status_checking" msgid="8026559918948285013">"तपासत आहे..."</string> <string name="tts_engine_settings_title" msgid="7849477533103566291">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> साठी सेटिंग्ज"</string> - <string name="tts_engine_settings_button" msgid="477155276199968948">"इंजिन सेटिंग्ज लाँच करा"</string> - <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"प्राधान्य इंजिन"</string> + <string name="tts_engine_settings_button" msgid="477155276199968948">"इंजीन सेटिंग्ज लाँच करा"</string> + <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"प्राधान्य इंजीन"</string> <string name="tts_general_section_title" msgid="8919671529502364567">"सामान्य"</string> <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"उच्चार पिच रीसेट करा"</string> <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"डीफॉल्टवर मजकूर ज्या पिचवर बोलला जातो तो रीसेट करा."</string> @@ -576,8 +576,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"४G+"</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-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-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 61374e8b25ba..1dcb3cfd349d 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/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-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-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index fe6204bdd434..13f69c1b793e 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/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">"Mobildata er slått av"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Ikke konfigurert til å bruke data"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Ingen telefon."</string> 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-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index b7ea8a09c6e4..718ce5de62f8 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/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">"Mobiele data uit"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Gebruik van gegevens is niet ingesteld"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Geen telefoonsignaal."</string> 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-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 2ae7d7130e8a..e04e2017265f 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/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-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-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index fc26f5906309..f24b52bb409c 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/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">"Dados móveis desativados"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Sem configuração para uso de dados"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Sem telefone."</string> 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-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 7ff1ba4a9f92..42ad0fece020 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/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">"WFO"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Dados móveis desativados"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Não definido para utilizar dados"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Sem telefone."</string> 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-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index fc26f5906309..f24b52bb409c 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/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">"Dados móveis desativados"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Sem configuração para uso de dados"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Sem telefone."</string> 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-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index be0193ce9a1e..0743fe99056c 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -577,8 +577,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">"Rețeaua Wi‑Fi a operatorului"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Date mobile dezactivate"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Nu este setat pentru a folosi datele"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Nu există semnal pentru telefon."</string> 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-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index fad3591d7b36..d19438aceb16 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/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">"Мобильный Интернет отключен"</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-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-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 69a918a898be..15fe8c8370e8 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/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-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-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index f98237c27537..ee53b7cfbcec 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/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">"Wi‑Fi operátora"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobilné dáta sú vypnuté"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Nie je nastavené na používanie dát"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Žiadna telefónna sieť."</string> 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-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 710fbdb14901..66d33a7bf51a 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/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">"Omrežje Wi‑Fi operaterja"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Prenos podatkov v mobilnem omrežju je izklopljen"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Ni nastavljeno za uporabo prenosa podatkov"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Ni telefona."</string> 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-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index d134f8ad5c3e..ce74b84aa1e4 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -577,8 +577,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">"WiFi мобилног оператера"</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-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-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index e90a915c81b3..eacb7a80cdb8 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/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">"Operatörens Wi-Fi"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobildata har inaktiverats"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Inte inställd på mobildata"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Ingen telefon."</string> 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-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 653beef22c5c..e7045a72b51b 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/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">"Umezima data ya mtandao wa simu"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Haijawekewa mipangilio ya kutumia data"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Hakuna simu"</string> 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-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index b75818a873b0..06c7ccb98120 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/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-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..5e880a95c2de 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -140,15 +140,15 @@ <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"ఓపెన్ నెట్వర్క్"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"సురక్షిత నెట్వర్క్"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> - <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"తీసివేయబడిన అనువర్తనాలు"</string> - <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"తీసివేయబడిన అనువర్తనాలు మరియు వినియోగదారులు"</string> + <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"తీసివేయబడిన యాప్లు"</string> + <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"తీసివేయబడిన యాప్లు మరియు వినియోగదారులు"</string> <string name="data_usage_ota" msgid="7984667793701597001">"సిస్టమ్ అప్డేట్లు"</string> <string name="tether_settings_title_usb" msgid="3728686573430917722">"USB టీథరింగ్"</string> <string name="tether_settings_title_wifi" msgid="4803402057533895526">"పోర్టబుల్ హాట్స్పాట్"</string> <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"బ్లూటూత్ టెథెరింగ్"</string> <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"టీథరింగ్"</string> <string name="tether_settings_title_all" msgid="8910259483383010470">"టీథరింగ్ & పోర్టబుల్ హాట్స్పాట్"</string> - <string name="managed_user_title" msgid="449081789742645723">"అన్ని కార్యాలయ అనువర్తనాలు"</string> + <string name="managed_user_title" msgid="449081789742645723">"అన్ని కార్యాలయ యాప్లు"</string> <string name="user_guest" msgid="6939192779649870792">"అతిథి"</string> <string name="unknown" msgid="3544487229740637809">"తెలియదు"</string> <string name="running_process_item_user_label" msgid="3988506293099805796">"వినియోగదారు: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string> @@ -313,14 +313,14 @@ <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"బ్లూటూత్ Gabeldorsche ఫీచర్ స్ట్యాక్ను ఎనేబుల్ చేస్తుంది."</string> <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"మెరుగైన కనెక్టివిటీ ఫీచర్ను ఎనేబుల్ చేస్తుంది."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"స్థానిక టెర్మినల్"</string> - <string name="enable_terminal_summary" msgid="2481074834856064500">"స్థానిక షెల్ ప్రాప్యతను అందించే టెర్మినల్ అనువర్తనాన్ని ప్రారంభించు"</string> + <string name="enable_terminal_summary" msgid="2481074834856064500">"స్థానిక షెల్ ప్రాప్యతను అందించే టెర్మినల్ యాప్ను ప్రారంభించు"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP తనిఖీ"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP తనిఖీ ప్రవర్తనను సెట్ చేయండి"</string> <string name="debug_debugging_category" msgid="535341063709248842">"డీబగ్గింగ్"</string> <string name="debug_app" msgid="8903350241392391766">"డీబగ్ యాప్ను ఎంచుకోండి"</string> <string name="debug_app_not_set" msgid="1934083001283807188">"డీబగ్ యాప్ సెట్ చేయబడలేదు"</string> <string name="debug_app_set" msgid="6599535090477753651">"డీబగ్గింగ్ యాప్: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> - <string name="select_application" msgid="2543228890535466325">"అనువర్తనాన్ని ఎంచుకోండి"</string> + <string name="select_application" msgid="2543228890535466325">"యాప్ను ఎంచుకోండి"</string> <string name="no_application" msgid="9038334538870247690">"ఏదీ వద్దు"</string> <string name="wait_for_debugger" msgid="7461199843335409809">"డీబగ్గర్ కోసం వేచి ఉండండి"</string> <string name="wait_for_debugger_summary" msgid="6846330006113363286">"డీబగ్ చేయబడిన యాప్ అమలు కావడానికి ముందు జోడించాల్సిన డీబగ్గర్ కోసం వేచి ఉంటుంది"</string> @@ -547,7 +547,7 @@ <string name="user_new_profile_name" msgid="2405500423304678841">"కొత్త ప్రొఫైల్"</string> <string name="user_info_settings_title" msgid="6351390762733279907">"వినియోగదారు సమాచారం"</string> <string name="profile_info_settings_title" msgid="105699672534365099">"ప్రొఫైల్ సమాచారం"</string> - <string name="user_need_lock_message" msgid="4311424336209509301">"మీరు పరిమితం చేయబడిన ప్రొఫైల్ను సృష్టించడానికి ముందు, మీ అనువర్తనాలు మరియు వ్యక్తిగత డేటాను రక్షించడానికి స్క్రీన్ లాక్ను సెటప్ చేయాల్సి ఉంటుంది."</string> + <string name="user_need_lock_message" msgid="4311424336209509301">"మీరు పరిమితం చేయబడిన ప్రొఫైల్ను సృష్టించడానికి ముందు, మీ యాప్లు మరియు వ్యక్తిగత డేటాను రక్షించడానికి స్క్రీన్ లాక్ను సెటప్ చేయాల్సి ఉంటుంది."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"లాక్ను సెట్ చేయి"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>కు మార్చు"</string> <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"కొత్త యూజర్ను క్రియేట్ చేస్తోంది…"</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">"మొబైల్ డేటా ఆఫ్లో ఉంది"</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-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index a010c1072884..6316452511b4 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/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-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-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 4485384d8880..0aabbe5f96ab 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/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">"Naka-off ang mobile data"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Hindi nakatakdang gumamit ng data"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Walang telepono."</string> 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-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 081a11515bd0..44c8f13acb96 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/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">"Mobil veri kapalı"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Veri kullanmak üzere ayarlanmadı"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Telefon sinyali yok."</string> 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-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index b4e533d39f3a..7851111910d2 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/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">"Мережа Wi-Fi оператора"</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-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-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 4787aeb90a7c..c2a96c6a810e 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/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">"Mobil internet yoqilmagan"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Maʼlumotlardan foydalanish uchun sozlanmagan"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Signal yo‘q."</string> 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-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index df975b316b8a..259642466187 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/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ắt dữ liệu di động"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Chưa được đặt để sử dụng dữ liệu"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Không có điện thoại nào."</string> 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-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index e1b6047793d1..60afd6d8352f 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/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-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-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index e095afd2f3f7..925f73870ea3 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/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">"流動網絡供應商 Wi-Fi"</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-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-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index a1dc7f1515b7..e40d3519788d 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/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-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/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index a04f47cfc629..c304c14f6073 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -576,8 +576,7 @@ <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string> <string name="data_connection_lte" msgid="7675461204366364124">"I-LTE"</string> <string name="data_connection_lte_plus" msgid="6643158654804916653">"I-LTE+"</string> - <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) --> - <skip /> + <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"I-CWF"</string> <string name="cell_data_off_content_description" msgid="2280700839891636498">"Idatha yeselula ivaliwe"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Akusethiwe ukuze kusetshenziswe idatha"</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Ayikho ifoni."</string> diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml index c63cf06cf75c..2b5e9cdc017d 100644 --- a/packages/SettingsLib/res/values/arrays.xml +++ b/packages/SettingsLib/res/values/arrays.xml @@ -291,7 +291,7 @@ <item>256K</item> <item>1M</item> <item>4M</item> - <item>16M</item> + <item>8M</item> </string-array> <!-- Titles for logd limit size lowram selection preference. [CHAR LIMIT=14] --> @@ -309,7 +309,7 @@ <item>262144</item> <item>1048576</item> <item>4194304</item> - <item>16777216</item> + <item>8388608</item> </string-array> <!-- Summaries for logd limit size selection preference. [CHAR LIMIT=50]--> @@ -319,7 +319,7 @@ <item>256K per log buffer</item> <item>1M per log buffer</item> <item>4M per log buffer</item> - <item>16M per log buffer</item> + <item>8M per log buffer</item> </string-array> <!-- Values for logpersist state selection preference. --> diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java index ac20ee14ced2..927da0ae8495 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java @@ -267,19 +267,28 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { permitted = dpm.isInputMethodPermittedByAdmin(admin.component, packageName, userId); } + + boolean permittedByParentAdmin = true; + EnforcedAdmin profileAdmin = null; int managedProfileId = getManagedProfileId(context, userId); - EnforcedAdmin profileAdmin = getProfileOrDeviceOwner(context, - getUserHandleOf(managedProfileId)); - boolean permittedByProfileAdmin = true; - if (profileAdmin != null) { - permittedByProfileAdmin = dpm.isInputMethodPermittedByAdmin(profileAdmin.component, - packageName, managedProfileId); + if (managedProfileId != UserHandle.USER_NULL) { + profileAdmin = getProfileOrDeviceOwner(context, getUserHandleOf(managedProfileId)); + // If the device is an organization-owned device with a managed profile, the + // managedProfileId will be used instead of the affected userId. This is because + // isInputMethodPermittedByAdmin is called on the parent DPM instance, which will + // return results affecting the personal profile. + if (profileAdmin != null && dpm.isOrganizationOwnedDeviceWithManagedProfile()) { + DevicePolicyManager parentDpm = sProxy.getParentProfileInstance(dpm, + UserManager.get(context).getUserInfo(managedProfileId)); + permittedByParentAdmin = parentDpm.isInputMethodPermittedByAdmin( + profileAdmin.component, packageName, managedProfileId); + } } - if (!permitted && !permittedByProfileAdmin) { + if (!permitted && !permittedByParentAdmin) { return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; } else if (!permitted) { return admin; - } else if (!permittedByProfileAdmin) { + } else if (!permittedByParentAdmin) { return profileAdmin; } return null; diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp index f6f9dba784a0..92e32d96cdf3 100644 --- a/packages/SettingsLib/tests/integ/Android.bp +++ b/packages/SettingsLib/tests/integ/Android.bp @@ -38,6 +38,7 @@ android_test { "androidx.test.espresso.core", "mockito-target-minus-junit4", "truth-prebuilt", + "SettingsLibSettingsSpinner", "SettingsLibUsageProgressBarPreference", ], diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java new file mode 100644 index 000000000000..53a382a9ebf6 --- /dev/null +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.widget; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; + +import androidx.preference.PreferenceViewHolder; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.runner.AndroidJUnit4; + +import com.android.settingslib.widget.settingsspinner.SettingsSpinner; +import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +public class SettingsSpinnerPreferenceTest { + + private Context mContext; + private PreferenceViewHolder mViewHolder; + private SettingsSpinner mSpinner; + private SettingsSpinnerPreference mSpinnerPreference; + + @Before + public void setUp() { + mContext = ApplicationProvider.getApplicationContext(); + mSpinnerPreference = new SettingsSpinnerPreference(mContext); + final LayoutInflater inflater = LayoutInflater.from(mContext); + final View rootView = inflater.inflate(mSpinnerPreference.getLayoutResource(), + new LinearLayout(mContext), false /* attachToRoot */); + mViewHolder = PreferenceViewHolder.createInstanceForTests(rootView); + mSpinner = (SettingsSpinner) mViewHolder.findViewById(R.id.spinner); + } + + @Test + public void onBindViewHolder_noSetSelection_getDefaultItem() { + final List<CharSequence> list = new ArrayList<>(); + list.add("TEST1"); + list.add("TEST2"); + list.add("TEST3"); + final SettingsSpinnerAdapter adapter = new SettingsSpinnerAdapter(mContext); + adapter.addAll(list); + mSpinnerPreference.setAdapter(adapter); + + mSpinnerPreference.onBindViewHolder(mViewHolder); + + assertThat(adapter).isEqualTo(mSpinner.getAdapter()); + assertThat(mSpinnerPreference.getSelectedItem()) + .isEqualTo(mSpinner.getAdapter().getItem(0)); + } + + @Test + public void onBindViewHolder_setSelection_getSelectedItem() { + final List<CharSequence> list = new ArrayList<>(); + list.add("TEST1"); + list.add("TEST2"); + list.add("TEST3"); + final SettingsSpinnerAdapter adapter = new SettingsSpinnerAdapter(mContext); + adapter.addAll(list); + mSpinnerPreference.setAdapter(adapter); + mSpinnerPreference.setSelection(1); + + mSpinnerPreference.onBindViewHolder(mViewHolder); + + assertThat(mSpinnerPreference.getSelectedItem()) + .isEqualTo(mSpinner.getAdapter().getItem(1)); + } +} 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 72997845f11c..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"/> @@ -157,6 +158,7 @@ <uses-permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS" /> <uses-permission android:name="android.permission.MANAGE_APP_PREDICTIONS" /> <uses-permission android:name="android.permission.MANAGE_SEARCH_UI" /> + <uses-permission android:name="android.permission.MANAGE_SMARTSPACE" /> <uses-permission android:name="android.permission.NETWORK_SETTINGS" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.SET_TIME" /> diff --git a/packages/SimAppDialog/res/values-mn/strings.xml b/packages/SimAppDialog/res/values-mn/strings.xml index f21b80bc759b..51298bbda34b 100644 --- a/packages/SimAppDialog/res/values-mn/strings.xml +++ b/packages/SimAppDialog/res/values-mn/strings.xml @@ -19,8 +19,8 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_name" msgid="8898068901680117589">"Sim аппын харилцах цонх"</string> <string name="install_carrier_app_title" msgid="334729104862562585">"Мобайл үйлчилгээг идэвхжүүлэх"</string> - <string name="install_carrier_app_description" msgid="4014303558674923797">"Та шинэ СИМ-ээ зөв ажиллуулахын тулд <xliff:g id="ID_1">%1$s</xliff:g> аппыг суулгах хэрэгтэй болно"</string> - <string name="install_carrier_app_description_default" msgid="7356830245205847840">"Та шинэ СИМ-ээ зөв ажиллуулахын тулд оператор компанийхаа аппыг суулгах хэрэгтэй болно"</string> + <string name="install_carrier_app_description" msgid="4014303558674923797">"Та шинэ SIM-ээ зөв ажиллуулахын тулд <xliff:g id="ID_1">%1$s</xliff:g> аппыг суулгах хэрэгтэй болно"</string> + <string name="install_carrier_app_description_default" msgid="7356830245205847840">"Та шинэ SIM-ээ зөв ажиллуулахын тулд оператор компанийхаа аппыг суулгах хэрэгтэй болно"</string> <string name="install_carrier_app_defer_action" msgid="2558576736886876209">"Одоо биш"</string> <string name="install_carrier_app_download_action" msgid="7859229305958538064">"Апп татах"</string> </resources> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index e036d87f2492..39fbb34f4877 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -116,6 +116,7 @@ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <uses-permission android:name="android.permission.MONITOR_INPUT" /> <uses-permission android:name="android.permission.INPUT_CONSUMER" /> + <uses-permission android:name="android.permission.USE_BACKGROUND_BLUR" /> <!-- DreamManager --> <uses-permission android:name="android.permission.READ_DREAM_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 0beb286b6f63..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nuwe gebruiker"</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_wifi_not_connected" msgid="4071097522427039160">"Nie gekoppel nie"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Geen netwerk nie"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi af"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index f56e84ae1606..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> @@ -354,6 +358,12 @@ <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_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-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index aaaf7788c5c3..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> @@ -358,6 +362,12 @@ <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_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-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 7e3e3cb04280..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> @@ -353,7 +357,12 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ব্যৱহাৰকাৰী"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"নতুন ব্যৱহাৰকাৰী"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ৱাই-ফাই"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <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_wifi_not_connected" msgid="4071097522427039160">"সংযোগ হৈ থকা নাই"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"নেটৱৰ্ক নাই"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 6c22cc34b83e..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Yeni istifadəçi"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"İnternet"</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_wifi_not_connected" msgid="4071097522427039160">"Bağlantı yoxdur"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Şəbəkə yoxdur"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi sönülüdü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 2f3d6d28526e..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> @@ -355,6 +359,12 @@ <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_wifi_not_connected" msgid="4071097522427039160">"Veza nije uspostavljena"</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-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 925e86a8916b..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> @@ -356,6 +360,12 @@ <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_wifi_not_connected" msgid="4071097522427039160">"Няма падключэння"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Няма сеткi"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi адключаны"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 6a683c742931..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> @@ -354,6 +358,12 @@ <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_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-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 953ec006016b..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,6 +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> + <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 6bf185230602..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,6 +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> + <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 71105cc55b50..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Usuari nou"</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_wifi_not_connected" msgid="4071097522427039160">"Desconnectat"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No hi ha cap xarxa"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desconnectada"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 65e676737ca2..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> @@ -356,6 +360,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nový uživatel"</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_wifi_not_connected" msgid="4071097522427039160">"Nepřipojeno"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Žádná síť"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi vypnuta"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index e50aa57e70cb..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Ny bruger"</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_wifi_not_connected" msgid="4071097522427039160">"Ikke forbundet"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Intet netværk"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi slået fra"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 3cdebf9c80d9..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Neuer Nutzer"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WLAN"</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_wifi_not_connected" msgid="4071097522427039160">"Nicht verbunden"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Kein Netz"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WLAN aus"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index c6ae7f4b0c51..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,6 +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> + <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 ea25ec6e771c..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> @@ -354,6 +358,12 @@ <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_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-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index a373a5c0061d..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> @@ -354,6 +358,12 @@ <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_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-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index ea25ec6e771c..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> @@ -354,6 +358,12 @@ <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_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-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index ea25ec6e771c..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> @@ -354,6 +358,12 @@ <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_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-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 101d11245ffc..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,6 +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> + <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 1fc1609c7dcc..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Usuario nuevo"</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_wifi_not_connected" msgid="4071097522427039160">"Sin conexión"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sin red"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desactivada"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index dd36a64812ab..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nuevo usuario"</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_wifi_not_connected" msgid="4071097522427039160">"No conectado"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No hay red."</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desactivado"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index fb27be994265..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Uus kasutaja"</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_wifi_not_connected" msgid="4071097522427039160">"Ühendus puudub"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Võrku pole"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi-ühendus on väljas"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index d8b21c633dfa..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Erabiltzaile berria"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wifia"</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_wifi_not_connected" msgid="4071097522427039160">"Konektatu gabe"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ez dago sarerik"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi konexioa desaktibatuta"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index df306f83e16e..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,6 +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> + <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 4e0af373f572..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Uusi käyttäjä"</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_wifi_not_connected" msgid="4071097522427039160">"Ei yhteyttä"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ei verkkoa"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi-yhteys pois käytöstä"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 8c944a4a0e0f..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,6 +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> + <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 7ad239dc4ee2..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> @@ -354,6 +358,12 @@ <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_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-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 145b3c06b387..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo usuario"</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_wifi_not_connected" msgid="4071097522427039160">"Non conectada"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Non hai rede"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wifi desactivada"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index ea7af17dcb6a..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> @@ -354,6 +358,12 @@ <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_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-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index a8bed5b26401..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> @@ -356,6 +360,12 @@ <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_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-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 4ffa5b2e04ff..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,6 +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> + <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 cff5b0dc65b3..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Új felhasználó"</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_wifi_not_connected" msgid="4071097522427039160">"Nincs kapcsolat"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nincs hálózat"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi kikapcsolva"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 91ffe7308a91..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,6 +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> + <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 f9e7397993dc..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Pengguna baru"</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_wifi_not_connected" msgid="4071097522427039160">"Tidak Terhubung"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tidak Ada Jaringan"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Mati"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index db5dac43a42a..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nýr notandi"</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_wifi_not_connected" msgid="4071097522427039160">"Engin tenging"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ekkert net"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Slökkt á Wi-Fi"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 86e65bbc3629..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nuovo utente"</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_wifi_not_connected" msgid="4071097522427039160">"Non connessa"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nessuna rete"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi disattivato"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 8548dae09ee2..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> @@ -356,6 +360,12 @@ <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_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-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 6cd560854960..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> @@ -354,6 +358,12 @@ <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_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 OFF"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 52d9f0e77451..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,6 +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> + <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 fcf87434ad3b..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> @@ -354,6 +358,12 @@ <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_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-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index e92bf9bda905..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> @@ -354,6 +358,12 @@ <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_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-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index d7a0adc85935..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> @@ -354,6 +358,12 @@ <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_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-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 54bcfb978ac9..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> @@ -354,6 +358,12 @@ <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_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-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index d513380f9355..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> @@ -356,6 +360,12 @@ <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_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-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 7e595cd83176..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,6 +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> + <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 e0c27a96de44..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> @@ -356,6 +360,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Naujas naudotojas"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internetas"</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_wifi_not_connected" msgid="4071097522427039160">"Neprisijungta"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tinklo nėra"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"„Wi-Fi“ išjungta"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index ff36f51b5603..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> @@ -355,6 +359,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Jauns lietotājs"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internets"</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_wifi_not_connected" msgid="4071097522427039160">"Nav izveidots savienojums"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nav tīkla"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ir izslēgts"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 203d8b90ae52..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> @@ -354,6 +358,12 @@ <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_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-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 77925ae0c268..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> @@ -354,6 +358,12 @@ <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_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-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index c772294a6bdf..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> @@ -354,6 +358,12 @@ <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_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-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 965b8958b458..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> @@ -353,7 +357,12 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"वापरकर्ता"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"नवीन वापरकर्ता"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"वाय-फाय"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <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_wifi_not_connected" msgid="4071097522427039160">"कनेक्ट केले नाही"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"नेटवर्क नाही"</string> @@ -456,11 +465,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"प्रोफाईल दर्शवा"</string> <string name="user_add_user" msgid="4336657383006913022">"वापरकर्ता जोडा"</string> <string name="user_new_user_name" msgid="2019166282704195789">"नवीन वापरकर्ता"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"अतिथी सत्र संपायचे का?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"या सत्रातील सर्व अॅप्स आणि डेटा हटवला जाईल."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"सत्र संपवा"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"अतिथी, तुमचे पुन्हा स्वागत आहे!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"तुम्ही तुमचे सत्र सुरू ठेवू इच्छिता?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"येथून सुरू करा"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index d3e360147d0f..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Pengguna baharu"</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_wifi_not_connected" msgid="4071097522427039160">"Tidak Disambungkan"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tiada Rangkaian"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Dimatikan"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 0c1f9cbbc060..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> @@ -354,6 +358,12 @@ <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_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-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 565e7fea215d..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Ny bruker"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internett"</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_wifi_not_connected" msgid="4071097522427039160">"Ikke tilkoblet"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ingen nettverk"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi er av"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 9d95cf042dad..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> @@ -354,6 +358,12 @@ <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_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-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 f598fa3fae09..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nieuwe gebruiker"</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_wifi_not_connected" msgid="4071097522427039160">"Niet verbonden"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Geen netwerk"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wifi uit"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 1e07dd4b785e..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> @@ -353,7 +357,12 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ୟୁଜର୍"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ୱାଇ-ଫାଇ"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <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_wifi_not_connected" msgid="4071097522427039160">"ସଂଯୁକ୍ତ ହୋଇନାହିଁ"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ନେଟ୍ୱର୍କ ନାହିଁ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 7d5bc96830a5..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> @@ -353,7 +357,12 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ਵਰਤੋਂਕਾਰ"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ਵਾਈ-ਫਾਈ"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <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_wifi_not_connected" msgid="4071097522427039160">"ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ਕੋਈ ਨੈੱਟਵਰਕ ਨਹੀਂ"</string> @@ -456,11 +465,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ਪ੍ਰੋਫਾਈਲ ਦਿਖਾਓ"</string> <string name="user_add_user" msgid="4336657383006913022">"ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰੋ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ਕੀ ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਸਮਾਪਤ ਕਰਨਾ ਹੈ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ਇਸ ਸੈਸ਼ਨ ਵਿੱਚ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਜਾਏਗਾ।"</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ਸੈਸ਼ਨ ਸਮਾਪਤ ਕਰੋ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ਮਹਿਮਾਨ, ਫਿਰ ਤੁਹਾਡਾ ਸੁਆਗਤ ਹੈ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ਕੀ ਤੁਸੀਂ ਆਪਣਾ ਸੈਸ਼ਨ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ਸ਼ੁਰੂ ਕਰੋ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 561e70dd14cb..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> @@ -356,6 +360,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nowy użytkownik"</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_wifi_not_connected" msgid="4071097522427039160">"Brak połączenia"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Brak sieci"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi wyłączone"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 7fe53d348e15..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,6 +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> + <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 dd154e8ad684..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,6 +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> + <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 7fe53d348e15..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,6 +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> + <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 317fc098d522..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> @@ -355,6 +359,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Utilizator nou"</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_wifi_not_connected" msgid="4071097522427039160">"Neconectată"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nicio rețea"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi deconectat"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index bbb014e82b07..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> @@ -356,6 +360,12 @@ <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_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-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index a7a2bb7dbbff..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> @@ -354,6 +358,12 @@ <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_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-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 7eb5297a43f1..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> @@ -356,6 +360,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nový používateľ"</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_wifi_not_connected" msgid="4071097522427039160">"Nepripojené"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Žiadna sieť"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Sieť Wi‑Fi je vypnutá"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 6d23f26a6c8d..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> @@ -356,6 +360,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nov uporabnik"</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_wifi_not_connected" msgid="4071097522427039160">"Povezava ni vzpostavljena"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ni omrežja"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi izklopljen"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 7e89b93cec82..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Përdorues i ri"</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_wifi_not_connected" msgid="4071097522427039160">"Nuk është i lidhur"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nuk ka rrjet"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi është i çaktivizuar"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index af5175c2bbf6..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> @@ -355,6 +359,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Нови корисник"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</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_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">"WiFi је искључен"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 41bca7c90b32..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Ny användare"</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_wifi_not_connected" msgid="4071097522427039160">"Ej ansluten"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Inget nätverk"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi av"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index f122013cc150..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,6 +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> + <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 c076a031b93f..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> @@ -354,6 +358,12 @@ <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_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-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index fca51077d06f..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> @@ -354,6 +358,12 @@ <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_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-television/config.xml b/packages/SystemUI/res/values-television/config.xml index 0c8c5c443c4a..dec83d95f0be 100644 --- a/packages/SystemUI/res/values-television/config.xml +++ b/packages/SystemUI/res/values-television/config.xml @@ -43,6 +43,7 @@ <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item> <item>com.android.systemui.toast.ToastUI</item> <item>com.android.systemui.wmshell.WMShell</item> + <item>com.android.systemui.media.systemsounds.HomeSoundEffectController</item> </string-array> <!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. --> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index c909d379dedc..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> @@ -354,6 +358,12 @@ <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_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">"ปิด WiFi"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 2e75fa3de905..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Bagong 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_wifi_not_connected" msgid="4071097522427039160">"Hindi Nakakonekta"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Walang Network"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Naka-off ang Wi-Fi"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 98552b1b135d..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Yeni kullanıcı"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Kablosuz"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"İnternet"</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_wifi_not_connected" msgid="4071097522427039160">"Bağlı Değil"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ağ yok"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Kablosuz Kapalı"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index b70d36af64c2..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> @@ -356,6 +360,12 @@ <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_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-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index bbadf6a7d5f1..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> @@ -354,6 +358,12 @@ <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_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-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index a20676dca577..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,6 +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> + <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 5de7df6a5d69..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Người dùng mới"</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_wifi_not_connected" msgid="4071097522427039160">"Chưa được kết nối"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Không có mạng nào"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Tắt Wi-Fi"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 7f3f3943e3c5..55b524ea4cfc 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -63,9 +63,9 @@ <string name="usb_debugging_allow" msgid="1722643858015321328">"允许"</string> <string name="usb_debugging_secondary_user_title" msgid="7843050591380107998">"不允许使用 USB 调试功能"</string> <string name="usb_debugging_secondary_user_message" msgid="3740347841470403244">"目前已登录此设备的用户无法开启 USB 调试功能。要使用此功能,请切换为主要用户的帐号。"</string> - <string name="wifi_debugging_title" msgid="7300007687492186076">"要允许在此网络上进行无线调试吗?"</string> + <string name="wifi_debugging_title" msgid="7300007687492186076">"要允许通过此网络上进行无线调试吗?"</string> <string name="wifi_debugging_message" msgid="5461204211731802995">"网络名称 (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWLAN 地址 (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string> - <string name="wifi_debugging_always" msgid="2968383799517975155">"在此网络上始终允许"</string> + <string name="wifi_debugging_always" msgid="2968383799517975155">"始终允许通过此网络进行调试"</string> <string name="wifi_debugging_allow" msgid="4573224609684957886">"允许"</string> <string name="wifi_debugging_secondary_user_title" msgid="2493201475880517725">"不允许使用无线调试功能"</string> <string name="wifi_debugging_secondary_user_message" msgid="4492383073970079751">"目前已登录此设备的用户无法开启无线调试功能。要使用此功能,请切换为主要用户的帐号。"</string> @@ -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,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"新用户"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WLAN"</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_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">"WLAN:关闭"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 485f1cf786be..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> @@ -354,6 +358,12 @@ <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_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-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 1edeacd1a843..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> @@ -354,6 +358,12 @@ <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_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-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 58baebc27cc6..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> @@ -354,6 +358,12 @@ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Umsebenzisi omusha"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"I-Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"I-inthanethi"</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_wifi_not_connected" msgid="4071097522427039160">"Akuxhunyiwe"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ayikho inethiwekhi"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"I-Wi-Fi icimile"</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/flags.xml b/packages/SystemUI/res/values/flags.xml new file mode 100644 index 000000000000..db8bfec2bac5 --- /dev/null +++ b/packages/SystemUI/res/values/flags.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <bool name="are_flags_overrideable">true</bool> + + <bool name="flag_notification_pipeline2">false</bool> + <bool name="flag_notification_pipeline2_rendering">false</bool> + + <!-- b/171917882 --> + <bool name="flag_notification_twocolumn">false</bool> +</resources> 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/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index 77e568c66a9d..a40dc7abf063 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -255,6 +255,7 @@ public class KeyguardDisplayManager { private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; + private final Context mContext; private KeyguardClockSwitchController mKeyguardClockSwitchController; private View mClock; private int mUsableWidth; @@ -278,6 +279,7 @@ public class KeyguardDisplayManager { WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; setCancelable(false); + mContext = context; } @Override @@ -301,7 +303,7 @@ public class KeyguardDisplayManager { mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200; mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200; - setContentView(LayoutInflater.from(getContext()) + setContentView(LayoutInflater.from(mContext) .inflate(R.layout.keyguard_presentation, null)); // Logic to make the lock screen fullscreen 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/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java index 9f6c19bdf06f..55359ea70873 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java @@ -27,6 +27,7 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.globalactions.GlobalActionsComponent; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.dagger.KeyguardModule; +import com.android.systemui.media.systemsounds.HomeSoundEffectController; import com.android.systemui.power.PowerUI; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsModule; @@ -178,4 +179,10 @@ public abstract class SystemUIBinder { @IntoMap @ClassKey(WMShell.class) public abstract SystemUI bindWMShell(WMShell sysui); + + /** Inject into HomeSoundEffectController. */ + @Binds + @IntoMap + @ClassKey(HomeSoundEffectController.class) + public abstract SystemUI bindHomeSoundEffectController(HomeSoundEffectController sysui); } 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/flags/FeatureFlagReader.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java new file mode 100644 index 000000000000..b77fcc822b88 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.flags; + +import android.annotation.NonNull; +import android.content.res.Resources; +import android.provider.DeviceConfig; +import android.util.SparseArray; + +import androidx.annotation.BoolRes; +import androidx.annotation.Nullable; + +import com.android.systemui.R; +import com.android.systemui.assist.DeviceConfigHelper; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.statusbar.FeatureFlags; +import com.android.systemui.util.wrapper.BuildInfo; + +import java.util.concurrent.Executor; + +import javax.inject.Inject; +/** + * Reads and caches feature flags for quick access + * + * Feature flags must be defined as boolean resources. For example: + * + * {@code + * <bool name="flag_foo_bar_baz">false</bool> + * } + * + * It is strongly recommended that the name of the resource begin with "flag_". + * + * Flags can be overridden via adb on development builds. For example, to override the flag from the + * previous example, do the following: + * + * {@code + * $ adb shell device_config put systemui flag_foo_bar_baz true + * } + * + * Note that all storage keys begin with "flag_", even if their associated resId does not. + * + * Calls to this class should probably be wrapped by {@link FeatureFlags}. + */ +@SysUISingleton +public class FeatureFlagReader { + private final Resources mResources; + private final DeviceConfigHelper mDeviceConfig; + private final boolean mAreFlagsOverrideable; + + private final SparseArray<CachedFlag> mCachedFlags = new SparseArray<>(); + + @Inject + public FeatureFlagReader( + @Main Resources resources, + BuildInfo build, + DeviceConfigHelper deviceConfig, + @Background Executor executor) { + mResources = resources; + mDeviceConfig = deviceConfig; + mAreFlagsOverrideable = + build.isDebuggable() && mResources.getBoolean(R.bool.are_flags_overrideable); + + mDeviceConfig.addOnPropertiesChangedListener(executor, this::onPropertiesChanged); + } + + /** + * Returns true if the specified feature flag has been enabled. + * + * @param resId The backing boolean resource that determines the value of the flag. This value + * can be overridden via DeviceConfig on development builds. + */ + public boolean isEnabled(@BoolRes int resId) { + synchronized (mCachedFlags) { + CachedFlag cachedFlag = mCachedFlags.get(resId); + + if (cachedFlag == null) { + String name = resourceIdToFlagName(resId); + boolean value = mResources.getBoolean(resId); + if (mAreFlagsOverrideable) { + value = mDeviceConfig.getBoolean(flagNameToStorageKey(name), value); + } + + cachedFlag = new CachedFlag(name, value); + mCachedFlags.put(resId, cachedFlag); + } + + return cachedFlag.value; + } + } + + private void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { + synchronized (mCachedFlags) { + for (String key : properties.getKeyset()) { + String flagName = storageKeyToFlagName(key); + if (flagName != null) { + clearCachedFlag(flagName); + } + } + } + } + + private void clearCachedFlag(String flagName) { + for (int i = 0; i < mCachedFlags.size(); i++) { + CachedFlag flag = mCachedFlags.valueAt(i); + if (flag.name.equals(flagName)) { + mCachedFlags.removeAt(i); + break; + } + } + } + + private String resourceIdToFlagName(@BoolRes int resId) { + String resName = mResources.getResourceEntryName(resId); + if (resName.startsWith(RESNAME_PREFIX)) { + resName = resName.substring(RESNAME_PREFIX.length()); + } + return resName; + } + + private String flagNameToStorageKey(String flagName) { + if (flagName.startsWith(STORAGE_KEY_PREFIX)) { + return flagName; + } else { + return STORAGE_KEY_PREFIX + flagName; + } + } + + @Nullable + private String storageKeyToFlagName(String configName) { + if (configName.startsWith(STORAGE_KEY_PREFIX)) { + return configName.substring(STORAGE_KEY_PREFIX.length()); + } else { + return null; + } + } + + private static class CachedFlag { + public final String name; + public final boolean value; + + private CachedFlag(String name, boolean value) { + this.name = name; + this.value = value; + } + } + + private static final String STORAGE_KEY_PREFIX = "flag_"; + private static final String RESNAME_PREFIX = "flag_"; +} 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/media/systemsounds/HomeSoundEffectController.java b/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java new file mode 100644 index 000000000000..dd3d02a86672 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java @@ -0,0 +1,82 @@ +/* + * 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.media.systemsounds; + +import android.app.ActivityManager; +import android.app.WindowConfiguration; +import android.content.Context; +import android.media.AudioManager; + +import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.shared.system.TaskStackChangeListener; +import com.android.systemui.shared.system.TaskStackChangeListeners; + +import javax.inject.Inject; + +/** + * If a sound effect is defined for {@link android.media.AudioManager#FX_HOME}, a sound is played + * when the home task moves to front and the last task that moved to front was not the home task. + */ +@SysUISingleton +public class HomeSoundEffectController extends SystemUI { + + private final AudioManager mAudioManager; + private final TaskStackChangeListeners mTaskStackChangeListeners; + // Initialize true because home sound should not be played when the system boots. + private boolean mIsLastTaskHome = true; + + @Inject + public HomeSoundEffectController( + Context context, + AudioManager audioManager, + TaskStackChangeListeners taskStackChangeListeners) { + super(context); + mAudioManager = audioManager; + mTaskStackChangeListeners = taskStackChangeListeners; + } + + @Override + public void start() { + if (mAudioManager.isHomeSoundEffectEnabled()) { + mTaskStackChangeListeners.registerTaskStackListener( + new TaskStackChangeListener() { + @Override + public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) { + handleHomeTaskMovedToFront(taskInfo); + } + }); + } + } + + private boolean isHomeTask(ActivityManager.RunningTaskInfo taskInfo) { + return taskInfo.getActivityType() == WindowConfiguration.ACTIVITY_TYPE_HOME; + } + + /** + * To enable a home sound, check if the home app moves to front. + */ + private void handleHomeTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) { + boolean isCurrentTaskHome = isHomeTask(taskInfo); + // If the last task is home we don't want to play the home sound. This avoids playing + // the home sound after FallbackHome transitions to Home + if (!mIsLastTaskHome && isCurrentTaskHome) { + mAudioManager.playSoundEffect(AudioManager.FX_HOME); + } + mIsLastTaskHome = isCurrentTaskHome; + } +} 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/privacy/PrivacyChipBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt index 1d2e74703b42..eec69f98b9be 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt @@ -28,7 +28,7 @@ class PrivacyChipBuilder(private val context: Context, itemsList: List<PrivacyIt appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType }) .toList() .sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps - { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest) + { it.second.minOrNull() })) // Sort by "smallest" AppOpp (Location is largest) types = itemsList.map { it.privacyType }.distinct().sorted() } diff --git a/packages/SystemUI/src/com/android/systemui/qs/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 357702ada82b..211f5072bd1a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -63,6 +63,7 @@ import android.view.View; import android.view.ViewOutlineProvider; import android.view.ViewTreeObserver; import android.view.WindowInsets; +import android.view.accessibility.AccessibilityManager; import android.view.animation.AccelerateInterpolator; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; @@ -73,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; @@ -105,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 @@ -117,6 +117,7 @@ public class ScreenshotView extends FrameLayout implements private final DisplayMetrics mDisplayMetrics; private final float mCornerSizeX; private final float mDismissDeltaY; + private final AccessibilityManager mAccessibilityManager; private int mNavMode; private int mLeftInset; @@ -140,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; @@ -178,6 +179,8 @@ public class ScreenshotView extends FrameLayout implements mDisplayMetrics = new DisplayMetrics(); mContext.getDisplay().getRealMetrics(mDisplayMetrics); + + mAccessibilityManager = AccessibilityManager.getInstance(mContext); } /** @@ -292,6 +295,10 @@ public class ScreenshotView extends FrameLayout implements requestFocus(); } + View getScreenshotPreview() { + return mScreenshotPreview; + } + /** * Set up the logger and callback on dismissal. * @@ -331,8 +338,10 @@ public class ScreenshotView extends FrameLayout implements mScreenshotPreview.setScaleX(currentScale); mScreenshotPreview.setScaleY(currentScale); - mDismissButton.setAlpha(0); - mDismissButton.setVisibility(View.VISIBLE); + if (mAccessibilityManager.isEnabled()) { + mDismissButton.setAlpha(0); + mDismissButton.setVisibility(View.VISIBLE); + } AnimatorSet dropInAnimation = new AnimatorSet(); ValueAnimator flashInAnimator = ValueAnimator.ofFloat(0, 1); @@ -529,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) { @@ -605,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"); } @@ -665,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); @@ -692,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); @@ -719,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/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index bbc4b780bc52..964c499e4b42 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -16,78 +16,36 @@ package com.android.systemui.statusbar; -import android.annotation.NonNull; -import android.provider.DeviceConfig; -import android.util.ArrayMap; - +import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.qualifiers.Background; - -import java.util.Map; -import java.util.concurrent.Executor; +import com.android.systemui.flags.FeatureFlagReader; import javax.inject.Inject; /** * Class to manage simple DeviceConfig-based feature flags. * - * To enable or disable a flag, run: - * - * {@code - * $ adb shell device_config put systemui <key> <true|false> -* } - * - * You will probably need to restart systemui for the changes to be picked up: - * - * {@code - * $ adb shell am restart com.android.systemui - * } + * See {@link FeatureFlagReader} for instructions on defining and flipping flags. */ @SysUISingleton public class FeatureFlags { - private final Map<String, Boolean> mCachedDeviceConfigFlags = new ArrayMap<>(); + private final FeatureFlagReader mFlagReader; @Inject - public FeatureFlags(@Background Executor executor) { - DeviceConfig.addOnPropertiesChangedListener( - /* namespace= */ "systemui", - executor, - this::onPropertiesChanged); + public FeatureFlags(FeatureFlagReader flagReader) { + mFlagReader = flagReader; } public boolean isNewNotifPipelineEnabled() { - return getDeviceConfigFlag("notification.newpipeline.enabled", /* defaultValue= */ true); + return mFlagReader.isEnabled(R.bool.flag_notification_pipeline2); } public boolean isNewNotifPipelineRenderingEnabled() { - return isNewNotifPipelineEnabled() - && getDeviceConfigFlag("notification.newpipeline.rendering", /* defaultValue= */ - false); + return mFlagReader.isEnabled(R.bool.flag_notification_pipeline2_rendering); } - /** - * Flag used for guarding development of b/171917882. - */ + /** b/171917882 */ public boolean isTwoColumnNotificationShadeEnabled() { - return getDeviceConfigFlag("notification.twocolumn", /* defaultValue= */ false); - } - - private void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { - synchronized (mCachedDeviceConfigFlags) { - for (String key : properties.getKeyset()) { - mCachedDeviceConfigFlags.remove(key); - } - } - } - - private boolean getDeviceConfigFlag(String key, boolean defaultValue) { - synchronized (mCachedDeviceConfigFlags) { - Boolean flag = mCachedDeviceConfigFlags.get(key); - if (flag == null) { - flag = DeviceConfig.getBoolean(/* namespace= */ "systemui", key, defaultValue); - mCachedDeviceConfigFlags.put(key, flag); - } - return flag; - } + return mFlagReader.isEnabled(R.bool.flag_notification_twocolumn); } } 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/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 6cd7a74cdf02..1dfd1f3ef69c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -32,7 +32,6 @@ import com.android.systemui.R; * Utility class to calculate the clock position and top padding of notifications on Keyguard. */ public class KeyguardClockPositionAlgorithm { - /** * How much the clock height influences the shade position. * 0 means nothing, 1 means move the shade up by the height of the clock @@ -81,6 +80,11 @@ public class KeyguardClockPositionAlgorithm { private int mMinTopMargin; /** + * Minimum top inset (in pixels) to avoid overlap with any display cutouts. + */ + private int mCutoutTopInset = 0; + + /** * Maximum bottom padding to avoid overlap with {@link KeyguardBottomAreaView} or * the ambient indication. */ @@ -172,7 +176,7 @@ public class KeyguardClockPositionAlgorithm { float panelExpansion, int parentHeight, int keyguardStatusHeight, int clockPreferredY, boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float emptyDragAmount, boolean bypassEnabled, int unlockedStackScrollerPadding, boolean showLockIcon, - float qsExpansion) { + float qsExpansion, int cutoutTopInset) { mMinTopMargin = statusBarMinHeight + (showLockIcon ? mContainerTopPaddingWithLockIcon : mContainerTopPaddingWithoutLockIcon); mMaxShadeBottom = maxShadeBottom; @@ -188,6 +192,7 @@ public class KeyguardClockPositionAlgorithm { mBypassEnabled = bypassEnabled; mUnlockedStackScrollerPadding = unlockedStackScrollerPadding; mQsExpansion = qsExpansion; + mCutoutTopInset = cutoutTopInset; } public void run(Result result) { @@ -270,10 +275,13 @@ public class KeyguardClockPositionAlgorithm { float darkAmount = mBypassEnabled && !mHasCustomClock ? 1.0f : mDarkAmount; - // TODO(b/12836565) - prototyping only adjustment if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) { - // This will keep the clock at the top - clockYDark = (int) (clockY + burnInPreventionOffsetY()); + // This will keep the clock at the top but out of the cutout area + float shift = 0; + if (clockY - mBurnInPreventionOffsetYLargeClock < mCutoutTopInset) { + shift = mCutoutTopInset - (clockY - mBurnInPreventionOffsetYLargeClock); + } + clockYDark = clockY + burnInPreventionOffsetY() + shift; } return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mEmptyDragAmount); } 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..3db6c80cf4ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -50,6 +50,7 @@ import android.os.PowerManager; import android.os.SystemClock; import android.util.Log; import android.util.MathUtils; +import android.view.DisplayCutout; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; @@ -344,6 +345,7 @@ public class NotificationPanelViewController extends PanelViewController { private float mEmptyDragAmount; private float mDownX; private float mDownY; + private int mDisplayCutoutTopInset = 0; // in pixels private final KeyguardClockPositionAlgorithm mClockPositionAlgorithm = @@ -909,7 +911,8 @@ public class NotificationPanelViewController extends PanelViewController { hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount, bypassEnabled, getUnlockedStackScrollerPadding(), mUpdateMonitor.shouldShowLockIcon(), - getQsExpansionFraction()); + getQsExpansionFraction(), + mDisplayCutoutTopInset); mClockPositionAlgorithm.run(mClockPositionResult); mKeyguardStatusViewController.updatePosition( mClockPositionResult.clockX, mClockPositionResult.clockY, @@ -2408,6 +2411,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; @@ -3830,6 +3838,9 @@ public class NotificationPanelViewController extends PanelViewController { private class OnApplyWindowInsetsListener implements View.OnApplyWindowInsetsListener { public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { + final DisplayCutout displayCutout = v.getRootWindowInsets().getDisplayCutout(); + mDisplayCutoutTopInset = displayCutout != null ? displayCutout.getSafeInsetTop() : 0; + mNavigationBarBottomHeight = insets.getStableInsetBottom(); updateMaxHeadsUpTranslation(); return insets; 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/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 92d013eeda8e..f1ccde771c54 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -294,7 +294,9 @@ public class NetworkControllerImpl extends BroadcastReceiver } }; - mWifiManager.registerScanResultsCallback(mReceiverHandler::post, scanResultsCallback); + if (mWifiManager != null) { + mWifiManager.registerScanResultsCallback(mReceiverHandler::post, scanResultsCallback); + } ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback(){ private Network mLastNetwork; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index 1d778419cbaf..4f4a504ddc9c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -33,7 +33,6 @@ import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.IConnectivityManager; import android.net.Network; -import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.os.Handler; import android.os.RemoteException; @@ -75,12 +74,8 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi private static final String TAG = "SecurityController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private static final NetworkRequest REQUEST = new NetworkRequest.Builder() - .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) - .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) - .setUids(null) - .build(); + private static final NetworkRequest REQUEST = + new NetworkRequest.Builder().clearCapabilities().build(); private static final int NO_NETWORK = -1; private static final String VPN_BRANDED_META_DATA = "com.android.systemui.IS_BRANDED"; 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/util/wrapper/BuildInfo.java b/packages/SystemUI/src/com/android/systemui/util/wrapper/BuildInfo.java new file mode 100644 index 000000000000..5e68a15f8ec7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/BuildInfo.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.wrapper; + +import android.os.Build; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Testable wrapper around {@link Build}. + */ +@Singleton +public class BuildInfo { + @Inject + public BuildInfo() { + } + + /** @see Build#IS_DEBUGGABLE */ + public boolean isDebuggable() { + return Build.IS_DEBUGGABLE; + } +} 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/flags/FeatureFlagReaderTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java new file mode 100644 index 000000000000..c79037b761aa --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.flags; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.res.Resources; +import android.provider.DeviceConfig; + +import androidx.annotation.BoolRes; +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.assist.DeviceConfigHelper; +import com.android.systemui.util.wrapper.BuildInfo; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executor; + +@SmallTest +public class FeatureFlagReaderTest extends SysuiTestCase { + @Mock private Resources mResources; + @Mock private BuildInfo mBuildInfo; + @Mock private DeviceConfigHelper mDeviceConfig; + @Mock private Executor mBackgroundExecutor; + + private FeatureFlagReader mReader; + + @Captor private ArgumentCaptor<DeviceConfig.OnPropertiesChangedListener> mListenerCaptor; + private DeviceConfig.OnPropertiesChangedListener mPropChangeListener; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mDeviceConfig.getBoolean(anyString(), anyBoolean())) + .thenAnswer(invocation -> invocation.getArgument(1)); + + defineFlag(FLAG_RESID_0, false); + defineFlag(FLAG_RESID_1, true); + + initialize(true, true); + + verify(mDeviceConfig).addOnPropertiesChangedListener(any(), mListenerCaptor.capture()); + mPropChangeListener = mListenerCaptor.getValue(); + } + + private void initialize(boolean isDebuggable, boolean isOverrideable) { + when(mBuildInfo.isDebuggable()).thenReturn(isDebuggable); + when(mResources.getBoolean(R.bool.are_flags_overrideable)).thenReturn(isOverrideable); + mReader = new FeatureFlagReader(mResources, mBuildInfo, mDeviceConfig, mBackgroundExecutor); + } + + @Test + public void testCantOverrideIfNotDebuggable() { + // GIVEN that the build is not debuggable + initialize(false, true); + + // GIVEN that a flag has been overridden to true + overrideFlag(FLAG_RESID_0, true); + + // THEN the flag is still false + assertFalse(mReader.isEnabled(FLAG_RESID_0)); + } + + @Test + public void testCantOverrideIfNotOverrideable() { + // GIVEN that flags are not overrideable + initialize(true, false); + + // GIVEN that a flag has been overridden to true + overrideFlag(FLAG_RESID_0, true); + + // THEN the flag is still false + assertFalse(mReader.isEnabled(FLAG_RESID_0)); + } + + @Test + public void testReadFlags() { + assertFalse(mReader.isEnabled(FLAG_RESID_0)); + assertTrue(mReader.isEnabled(FLAG_RESID_1)); + } + + @Test + public void testOverrideFlags() { + // GIVEN that flags are overridden + overrideFlag(FLAG_RESID_0, true); + overrideFlag(FLAG_RESID_1, false); + + // THEN the reader returns the overridden values + assertTrue(mReader.isEnabled(FLAG_RESID_0)); + assertFalse(mReader.isEnabled(FLAG_RESID_1)); + } + + @Test + public void testThatFlagReadsAreCached() { + // GIVEN that a flag is overridden + overrideFlag(FLAG_RESID_0, true); + + // WHEN the flag is queried many times + mReader.isEnabled(FLAG_RESID_0); + mReader.isEnabled(FLAG_RESID_0); + mReader.isEnabled(FLAG_RESID_0); + mReader.isEnabled(FLAG_RESID_0); + + // THEN the underlying resource and override are only queried once + verify(mResources, times(1)).getBoolean(FLAG_RESID_0); + verify(mDeviceConfig, times(1)).getBoolean(fakeStorageKey(FLAG_RESID_0), false); + } + + @Test + public void testCachesAreClearedAfterPropsChange() { + // GIVEN a flag whose value has already been queried + assertFalse(mReader.isEnabled(FLAG_RESID_0)); + + // WHEN the value of the flag changes + overrideFlag(FLAG_RESID_0, true); + Map<String, String> changedMap = new HashMap<>(); + changedMap.put(fakeStorageKey(FLAG_RESID_0), "true"); + DeviceConfig.Properties properties = + new DeviceConfig.Properties("systemui", changedMap); + mPropChangeListener.onPropertiesChanged(properties); + + // THEN the new value is provided + assertTrue(mReader.isEnabled(FLAG_RESID_0)); + } + + private void defineFlag(int resId, boolean value) { + when(mResources.getBoolean(resId)).thenReturn(value); + when(mResources.getResourceEntryName(resId)).thenReturn(fakeStorageKey(resId)); + } + + private void overrideFlag(int resId, boolean value) { + when(mDeviceConfig.getBoolean(eq(fakeStorageKey(resId)), anyBoolean())) + .thenReturn(value); + } + + private String fakeStorageKey(@BoolRes int resId) { + return "flag_testname_" + resId; + } + + private static final int FLAG_RESID_0 = 47; + private static final int FLAG_RESID_1 = 48; +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/systemsounds/HomeSoundEffectControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/systemsounds/HomeSoundEffectControllerTest.java new file mode 100644 index 000000000000..3a77f7eec7f9 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/systemsounds/HomeSoundEffectControllerTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media.systemsounds; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.app.ActivityManager; +import android.app.WindowConfiguration; +import android.content.Context; +import android.media.AudioManager; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.shared.system.TaskStackChangeListener; +import com.android.systemui.shared.system.TaskStackChangeListeners; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class HomeSoundEffectControllerTest extends SysuiTestCase { + + private @Mock Context mContext; + private @Mock AudioManager mAudioManager; + private @Mock TaskStackChangeListeners mTaskStackChangeListeners; + private @Mock ActivityManager.RunningTaskInfo mStandardActivityTaskInfo; + private @Mock ActivityManager.RunningTaskInfo mHomeActivityTaskInfo; + + private HomeSoundEffectController mController; + private TaskStackChangeListener mTaskStackChangeListener; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + doReturn(WindowConfiguration.ACTIVITY_TYPE_STANDARD).when( + mStandardActivityTaskInfo).getActivityType(); + doReturn(WindowConfiguration.ACTIVITY_TYPE_HOME).when( + mHomeActivityTaskInfo).getActivityType(); + + mController = new HomeSoundEffectController(mContext, mAudioManager, + mTaskStackChangeListeners); + } + + @Test + public void testHomeSoundEffectNotPlayedWhenHomeFirstMovesToFront() { + // When HomeSoundEffectController is started and the home sound effect is enabled, + startController(true /* isHomeSoundEffectEnabled */); + + // And the home task moves to the front, + mTaskStackChangeListener.onTaskMovedToFront(mHomeActivityTaskInfo); + + // Then no home sound effect should be played. + verify(mAudioManager, never()).playSoundEffect(AudioManager.FX_HOME); + } + + @Test + public void testHomeSoundEffectPlayedWhenEnabled() { + // When HomeSoundEffectController is started and the home sound effect is enabled, + startController(true /* isHomeSoundEffectEnabled */); + + // And first a task different from the home task moves to front, + mTaskStackChangeListener.onTaskMovedToFront(mStandardActivityTaskInfo); + + // And the home task moves to the front, + mTaskStackChangeListener.onTaskMovedToFront(mHomeActivityTaskInfo); + + // Then the home sound effect should be played. + verify(mAudioManager).playSoundEffect(AudioManager.FX_HOME); + } + + @Test + public void testHomeSoundEffectNotPlayedTwiceInRow() { + // When HomeSoundEffectController is started and the home sound effect is enabled, + startController(true /* isHomeSoundEffectEnabled */); + + // And first a task different from the home task moves to front, + mTaskStackChangeListener.onTaskMovedToFront(mStandardActivityTaskInfo); + + // And the home task moves to the front, + mTaskStackChangeListener.onTaskMovedToFront(mHomeActivityTaskInfo); + + // Then the home sound effect should be played. + verify(mAudioManager).playSoundEffect(AudioManager.FX_HOME); + + // If the home task moves to front a second time in a row, + mTaskStackChangeListener.onTaskMovedToFront(mHomeActivityTaskInfo); + + // Then no home sound effect should be played. + verify(mAudioManager, times(1)).playSoundEffect(AudioManager.FX_HOME); + } + + @Test + public void testHomeSoundEffectNotPlayedWhenNonHomeTaskMovesToFront() { + // When HomeSoundEffectController is started and the home sound effect is enabled, + startController(true /* isHomeSoundEffectEnabled */); + + // And a standard, non-home task, moves to the front, + mTaskStackChangeListener.onTaskMovedToFront(mStandardActivityTaskInfo); + + // Then no home sound effect should be played. + verify(mAudioManager, never()).playSoundEffect(AudioManager.FX_HOME); + } + + @Test + public void testHomeSoundEffectDisabled() { + // When HomeSoundEffectController is started and the home sound effect is disabled, + startController(false /* isHomeSoundEffectEnabled */); + + // Then no TaskStackListener should be registered + verify(mTaskStackChangeListeners, never()).registerTaskStackListener( + any(TaskStackChangeListener.class)); + } + + /** + * Sets {@link AudioManager#isHomeSoundEffectEnabled()} and starts HomeSoundEffectController. + * If the home sound effect is enabled, the registered TaskStackChangeListener is extracted. + */ + private void startController(boolean isHomeSoundEffectEnabled) { + // Configure home sound effect to be enabled + doReturn(isHomeSoundEffectEnabled).when(mAudioManager).isHomeSoundEffectEnabled(); + + mController.start(); + + if (isHomeSoundEffectEnabled) { + // Construct controller. Save the TaskStackListener for injecting events. + final ArgumentCaptor<TaskStackChangeListener> listenerCaptor = + ArgumentCaptor.forClass(TaskStackChangeListener.class); + verify(mTaskStackChangeListeners).registerTaskStackListener(listenerCaptor.capture()); + mTaskStackChangeListener = listenerCaptor.getValue(); + } + } +} 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/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java index c7c1823f6a29..ee1d758e7ae2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java @@ -52,6 +52,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private boolean mHasCustomClock; private boolean mHasVisibleNotifs; private float mQsExpansion; + private int mCutoutTopInset = 0; // in pixels @Before public void setUp() { @@ -394,7 +395,8 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight, mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, mPreferredClockY, mHasCustomClock, mHasVisibleNotifs, mDark, ZERO_DRAG, false /* bypassEnabled */, - 0 /* unlockedStackScrollerPadding */, false /* udfpsEnrolled */, mQsExpansion); + 0 /* unlockedStackScrollerPadding */, false /* udfpsEnrolled */, + mQsExpansion, mCutoutTopInset); mClockPositionAlgorithm.run(mClockPosition); } } 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 997e49fbad14..aada21dfcc1e 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); } @@ -6000,23 +6003,21 @@ public class ActivityManagerService extends IActivityManager.Stub final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated, String abiOverride, int zygotePolicyFlags) { return addAppLocked(info, customProcess, isolated, false /* disableHiddenApiChecks */, - false /* mountExtStorageFull */, abiOverride, zygotePolicyFlags); + abiOverride, zygotePolicyFlags); } @GuardedBy("this") final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated, - boolean disableHiddenApiChecks, boolean mountExtStorageFull, String abiOverride, - int zygotePolicyFlags) { + boolean disableHiddenApiChecks, String abiOverride, int zygotePolicyFlags) { return addAppLocked(info, customProcess, isolated, disableHiddenApiChecks, - false /* disableTestApiChecks */, mountExtStorageFull, abiOverride, - zygotePolicyFlags); + false /* disableTestApiChecks */, abiOverride, zygotePolicyFlags); } // TODO: Move to ProcessList? @GuardedBy("this") final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated, boolean disableHiddenApiChecks, boolean disableTestApiChecks, - boolean mountExtStorageFull, String abiOverride, int zygotePolicyFlags) { + String abiOverride, int zygotePolicyFlags) { ProcessRecord app; if (!isolated) { app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName, @@ -6051,8 +6052,7 @@ public class ActivityManagerService extends IActivityManager.Stub mPersistentStartingProcesses.add(app); mProcessList.startProcessLocked(app, new HostingRecord("added application", customProcess != null ? customProcess : app.processName), - zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks, - mountExtStorageFull, abiOverride); + zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks, abiOverride); } return app; @@ -7609,10 +7609,10 @@ public class ActivityManagerService extends IActivityManager.Stub crashInfo.throwLineNumber); FrameworkStatsLog.write(FrameworkStatsLog.APP_CRASH_OCCURRED, - Binder.getCallingUid(), + (r != null) ? r.uid : -1, eventType, processName, - Binder.getCallingPid(), + (r != null) ? r.pid : -1, (r != null && r.info != null) ? r.info.packageName : "", (r != null && r.info != null) ? (r.info.isInstantApp() ? FrameworkStatsLog.APP_CRASH_OCCURRED__IS_INSTANT_APP__TRUE @@ -8302,6 +8302,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 +8723,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 +9298,8 @@ public class ActivityManagerService extends IActivityManager.Stub TimeUtils.formatDuration(ptw.duration, pw); pw.print(" "); pw.println(ptw.tag); + pw.print(" "); + pw.print(ptw.type); } } } @@ -14349,10 +14353,6 @@ public class ActivityManagerService extends IActivityManager.Stub "disable hidden API checks"); } - // TODO(b/158750470): remove - final boolean mountExtStorageFull = isCallerShell() - && (flags & INSTR_FLAG_DISABLE_ISOLATED_STORAGE) != 0; - final long origId = Binder.clearCallingIdentity(); ProcessRecord app; @@ -14368,8 +14368,7 @@ public class ActivityManagerService extends IActivityManager.Stub UsageEvents.Event.SYSTEM_INTERACTION); } app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks, - disableTestApiChecks, mountExtStorageFull, abiOverride, - ZYGOTE_POLICY_FLAG_EMPTY); + disableTestApiChecks, abiOverride, ZYGOTE_POLICY_FLAG_EMPTY); } @@ -15357,11 +15356,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 +15387,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 +15404,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 +15713,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 +16007,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/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 6f6cad043a42..47b7e1b5fa41 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -1737,7 +1737,7 @@ public final class ProcessList { @GuardedBy("mService") boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord, int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks, - boolean mountExtStorageFull, String abiOverride) { + String abiOverride) { if (app.pendingStart) { return true; } @@ -2331,7 +2331,7 @@ public final class ProcessList { int zygotePolicyFlags, String abiOverride) { return startProcessLocked(app, hostingRecord, zygotePolicyFlags, false /* disableHiddenApiChecks */, false /* disableTestApiChecks */, - false /* mountExtStorageFull */, abiOverride); + abiOverride); } @GuardedBy("mService") 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 db7554185b9a..6d90eaafcf77 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -632,19 +632,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); + } } } @@ -767,13 +778,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, @@ -1150,6 +1192,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 + } + } } /** @@ -1214,6 +1267,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); } @@ -1254,9 +1338,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")); @@ -1878,6 +1966,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/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 08eeda20b4ba..7115c9ad5a05 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -7724,20 +7724,24 @@ public class AudioService extends IAudioService.Stub private class MyHdmiControlStatusChangeListenerCallback implements HdmiControlManager.HdmiControlStatusChangeListener { - public void onStatusChange(boolean isCecEnabled, boolean isCecAvailable) { + public void onStatusChange(@HdmiControlManager.HdmiCecControl int isCecEnabled, + boolean isCecAvailable) { synchronized (mHdmiClientLock) { if (mHdmiManager == null) return; - updateHdmiCecSinkLocked(isCecEnabled ? isCecAvailable : false); + boolean cecEnabled = isCecEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED; + updateHdmiCecSinkLocked(cecEnabled ? isCecAvailable : false); } } }; private class MyHdmiCecVolumeControlFeatureListener implements HdmiControlManager.HdmiCecVolumeControlFeatureListener { - public void onHdmiCecVolumeControlFeature(boolean enabled) { + public void onHdmiCecVolumeControlFeature( + @HdmiControlManager.VolumeControl int hdmiCecVolumeControl) { synchronized (mHdmiClientLock) { if (mHdmiManager == null) return; - mHdmiCecVolumeControlEnabled = enabled; + mHdmiCecVolumeControlEnabled = + hdmiCecVolumeControl == HdmiControlManager.VOLUME_CONTROL_ENABLED; } } }; @@ -8454,28 +8458,29 @@ public class AudioService extends IAudioService.Stub } for (AudioMix mix : policyConfig.getMixes()) { // If mix is requesting privileged capture - if (mix.getRule().allowPrivilegedPlaybackCapture()) { - // then it must have CAPTURE_MEDIA_OUTPUT or CAPTURE_AUDIO_OUTPUT permission - requireCaptureAudioOrMediaOutputPerm |= true; - - // and its format must be low quality enough - String error = mix.canBeUsedForPrivilegedCapture(mix.getFormat()); - if (error != null) { - Log.e(TAG, error); + if (mix.getRule().allowPrivilegedMediaPlaybackCapture()) { + // then its format must be low quality enough + String privilegedMediaCaptureError = + mix.canBeUsedForPrivilegedMediaCapture(mix.getFormat()); + if (privilegedMediaCaptureError != null) { + Log.e(TAG, privilegedMediaCaptureError); return false; } + // and it must have CAPTURE_MEDIA_OUTPUT or CAPTURE_AUDIO_OUTPUT permission + requireCaptureAudioOrMediaOutputPerm |= true; - // If mix is trying to excplicitly capture USAGE_VOICE_COMMUNICATION - if (mix.containsMatchAttributeRuleForUsage( - AudioAttributes.USAGE_VOICE_COMMUNICATION)) { - // then it must have CAPTURE_USAGE_VOICE_COMMUNICATION_OUTPUT permission - // Note that for UID, USERID or EXCLDUE rules, the capture will be silenced - // in AudioPolicyMix - if (voiceCommunicationCaptureMixes == null) { - voiceCommunicationCaptureMixes = new ArrayList<AudioMix>(); - } - voiceCommunicationCaptureMixes.add(mix); + } + // If mix is trying to explicitly capture USAGE_VOICE_COMMUNICATION + if (mix.containsMatchAttributeRuleForUsage( + AudioAttributes.USAGE_VOICE_COMMUNICATION) + && (mix.getRouteFlags() == mix.ROUTE_FLAG_LOOP_BACK_RENDER)) { + // It must have CAPTURE_USAGE_VOICE_COMMUNICATION_OUTPUT permission + // Note that for UID, USERID or EXCLDUE rules, the capture will be silenced + // in AudioPolicyMix + if (voiceCommunicationCaptureMixes == null) { + voiceCommunicationCaptureMixes = new ArrayList<AudioMix>(); } + voiceCommunicationCaptureMixes.add(mix); } // If mix is RENDER|LOOPBACK, then an audio MediaProjection is enough @@ -8498,7 +8503,7 @@ public class AudioService extends IAudioService.Stub if (voiceCommunicationCaptureMixes != null && voiceCommunicationCaptureMixes.size() > 0) { if (!callerHasPermission( android.Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT)) { - Log.e(TAG, "Privileged audio capture for voice communication requires " + Log.e(TAG, "Audio capture for voice communication requires " + "CAPTURE_VOICE_COMMUNICATION_OUTPUT system permission"); return false; } 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/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index 2a814268da25..40d20734eb50 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -57,6 +57,7 @@ import com.android.server.wm.WindowManagerInternal; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -466,8 +467,13 @@ public class CameraServiceProxy extends SystemService streamProtos[i].firstCaptureLatencyMillis = streamStats.getStartLatencyMs(); streamProtos[i].maxHalBuffers = streamStats.getMaxHalBuffers(); streamProtos[i].maxAppBuffers = streamStats.getMaxAppBuffers(); + streamProtos[i].histogramType = streamStats.getHistogramType(); + streamProtos[i].histogramBins = streamStats.getHistogramBins(); + streamProtos[i].histogramCounts = streamStats.getHistogramCounts(); if (CameraServiceProxy.DEBUG) { + String histogramTypeName = + cameraHistogramTypeToString(streamProtos[i].histogramType); Slog.v(TAG, "Stream " + i + ": width " + streamProtos[i].width + ", height " + streamProtos[i].height + ", format " + streamProtos[i].format @@ -478,7 +484,12 @@ public class CameraServiceProxy extends SystemService + ", firstCaptureLatencyMillis " + streamProtos[i].firstCaptureLatencyMillis + ", maxHalBuffers " + streamProtos[i].maxHalBuffers - + ", maxAppBuffers " + streamProtos[i].maxAppBuffers); + + ", maxAppBuffers " + streamProtos[i].maxAppBuffers + + ", histogramType " + histogramTypeName + + ", histogramBins " + + Arrays.toString(streamProtos[i].histogramBins) + + ", histogramCounts " + + Arrays.toString(streamProtos[i].histogramCounts)); } } } @@ -797,4 +808,14 @@ public class CameraServiceProxy extends SystemService return "CAMERA_FACING_UNKNOWN"; } + private static String cameraHistogramTypeToString(int cameraHistogramType) { + switch (cameraHistogramType) { + case CameraStreamStats.HISTOGRAM_TYPE_CAPTURE_LATENCY: + return "HISTOGRAM_TYPE_CAPTURE_LATENCY"; + default: + break; + } + return "HISTOGRAM_TYPE_UNKNOWN"; + } + } 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/PacProxyInstaller.java b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java index aadaf4d9584f..5dc8c1a00eaf 100644 --- a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java +++ b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java @@ -16,6 +16,7 @@ package com.android.server.connectivity; +import android.annotation.NonNull; import android.annotation.WorkerThread; import android.app.AlarmManager; import android.app.PendingIntent; @@ -71,10 +72,6 @@ public class PacProxyInstaller { private static final int DELAY_LONG = 4; private static final long MAX_PAC_SIZE = 20 * 1000 * 1000; - // Return values for #setCurrentProxyScriptUrl - public static final boolean DONT_SEND_BROADCAST = false; - public static final boolean DO_SEND_BROADCAST = true; - private String mCurrentPac; @GuardedBy("mProxyLock") private volatile Uri mPacUrl = Uri.EMPTY; @@ -93,7 +90,7 @@ public class PacProxyInstaller { private volatile boolean mHasSentBroadcast; private volatile boolean mHasDownloaded; - private Handler mConnectivityHandler; + private final Handler mConnectivityHandler; private final int mProxyMessage; /** @@ -102,6 +99,13 @@ public class PacProxyInstaller { private final Object mProxyLock = new Object(); /** + * Lock ensuring consistency between the values of mHasSentBroadcast, mHasDownloaded, the + * last URL and port, and the broadcast message being sent with the correct arguments. + * TODO : this should probably protect all instances of these variables + */ + private final Object mBroadcastStateLock = new Object(); + + /** * Runnable to download PAC script. * The behavior relies on the assumption it always runs on mNetThread to guarantee that the * latest data fetched from mPacUrl is stored in mProxyService. @@ -146,7 +150,7 @@ public class PacProxyInstaller { } } - public PacProxyInstaller(Context context, Handler handler, int proxyMessage) { + public PacProxyInstaller(@NonNull Context context, @NonNull Handler handler, int proxyMessage) { mContext = context; mLastPort = -1; final HandlerThread netThread = new HandlerThread("android.pacproxyinstaller", @@ -176,31 +180,27 @@ public class PacProxyInstaller { * PacProxyInstaller will trigger a new broadcast when it is ready. * * @param proxy Proxy information that is about to be broadcast. - * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST */ - public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) { - if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) { - if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) { - // Allow to send broadcast, nothing to do. - return DO_SEND_BROADCAST; - } - mPacUrl = proxy.getPacFileUrl(); - mCurrentDelay = DELAY_1; - mHasSentBroadcast = false; - mHasDownloaded = false; - getAlarmManager().cancel(mPacRefreshIntent); - bind(); - return DONT_SEND_BROADCAST; - } else { - getAlarmManager().cancel(mPacRefreshIntent); - synchronized (mProxyLock) { - mPacUrl = Uri.EMPTY; - mCurrentPac = null; - if (mProxyService != null) { - unbind(); + public void setCurrentProxyScriptUrl(@NonNull ProxyInfo proxy) { + synchronized (mBroadcastStateLock) { + if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) { + if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) return; + mPacUrl = proxy.getPacFileUrl(); + mCurrentDelay = DELAY_1; + mHasSentBroadcast = false; + mHasDownloaded = false; + getAlarmManager().cancel(mPacRefreshIntent); + bind(); + } else { + getAlarmManager().cancel(mPacRefreshIntent); + synchronized (mProxyLock) { + mPacUrl = Uri.EMPTY; + mCurrentPac = null; + if (mProxyService != null) { + unbind(); + } } } - return DO_SEND_BROADCAST; } } @@ -275,6 +275,7 @@ public class PacProxyInstaller { getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent); } + @GuardedBy("mProxyLock") private void setCurrentProxyScript(String script) { if (mProxyService == null) { Log.e(TAG, "setCurrentProxyScript: no proxy service"); @@ -347,6 +348,9 @@ public class PacProxyInstaller { public void setProxyPort(int port) { if (mLastPort != -1) { // Always need to send if port changed + // TODO: Here lacks synchronization because this write cannot + // guarantee that it's visible from sendProxyIfNeeded() when + // it's called by a Runnable which is post by mNetThread. mHasSentBroadcast = false; } mLastPort = port; @@ -386,13 +390,15 @@ public class PacProxyInstaller { mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy)); } - private synchronized void sendProxyIfNeeded() { - if (!mHasDownloaded || (mLastPort == -1)) { - return; - } - if (!mHasSentBroadcast) { - sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort)); - mHasSentBroadcast = true; + private void sendProxyIfNeeded() { + synchronized (mBroadcastStateLock) { + if (!mHasDownloaded || (mLastPort == -1)) { + return; + } + if (!mHasSentBroadcast) { + sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort)); + mHasSentBroadcast = true; + } } } } diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java index d83ff837d9be..b618d2b99a63 100644 --- a/services/core/java/com/android/server/connectivity/ProxyTracker.java +++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java @@ -226,9 +226,9 @@ public class ProxyTracker { final ProxyInfo defaultProxy = getDefaultProxy(); final ProxyInfo proxyInfo = null != defaultProxy ? defaultProxy : ProxyInfo.buildDirectProxy("", 0, Collections.emptyList()); + mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo); - if (mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo) - == PacProxyInstaller.DONT_SEND_BROADCAST) { + if (!shouldSendBroadcast(proxyInfo)) { return; } if (DBG) Log.d(TAG, "sending Proxy Broadcast for " + proxyInfo); @@ -244,6 +244,13 @@ public class ProxyTracker { } } + private boolean shouldSendBroadcast(ProxyInfo proxy) { + if (Uri.EMPTY.equals(proxy.getPacFileUrl())) return false; + if (proxy.getPacFileUrl().equals(proxy.getPacFileUrl()) + && (proxy.getPort() > 0)) return true; + return true; + } + /** * Sets the global proxy in memory. Also writes the values to the global settings of the device. * 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/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java index 16695d1237b6..e854481985ce 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java @@ -291,6 +291,8 @@ public class HdmiCecConfig { return STORAGE_GLOBAL_SETTINGS; case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE: return STORAGE_GLOBAL_SETTINGS; + case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE: + return STORAGE_GLOBAL_SETTINGS; case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST: return STORAGE_SHARED_PREFS; case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING: @@ -309,6 +311,8 @@ public class HdmiCecConfig { return Global.HDMI_CEC_VERSION; case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE: return Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP; + case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE: + return Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED; case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST: return setting.getName(); case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING: @@ -323,13 +327,13 @@ public class HdmiCecConfig { @Storage int storage = getStorage(setting); String storageKey = getStorageKey(setting); if (storage == STORAGE_SYSPROPS) { - Slog.d(TAG, "Reading '" + storageKey + "' sysprop."); + HdmiLogger.debug("Reading '" + storageKey + "' sysprop."); return mStorageAdapter.retrieveSystemProperty(storageKey, defaultValue); } else if (storage == STORAGE_GLOBAL_SETTINGS) { - Slog.d(TAG, "Reading '" + storageKey + "' global setting."); + HdmiLogger.debug("Reading '" + storageKey + "' global setting."); return mStorageAdapter.retrieveGlobalSetting(storageKey, defaultValue); } else if (storage == STORAGE_SHARED_PREFS) { - Slog.d(TAG, "Reading '" + storageKey + "' shared preference."); + HdmiLogger.debug("Reading '" + storageKey + "' shared preference."); return mStorageAdapter.retrieveSharedPref(storageKey, defaultValue); } return null; @@ -339,13 +343,13 @@ public class HdmiCecConfig { @Storage int storage = getStorage(setting); String storageKey = getStorageKey(setting); if (storage == STORAGE_SYSPROPS) { - Slog.d(TAG, "Setting '" + storageKey + "' sysprop."); + HdmiLogger.debug("Setting '" + storageKey + "' sysprop."); mStorageAdapter.storeSystemProperty(storageKey, value); } else if (storage == STORAGE_GLOBAL_SETTINGS) { - Slog.d(TAG, "Setting '" + storageKey + "' global setting."); + HdmiLogger.debug("Setting '" + storageKey + "' global setting."); mStorageAdapter.storeGlobalSetting(storageKey, value); } else if (storage == STORAGE_SHARED_PREFS) { - Slog.d(TAG, "Setting '" + storageKey + "' shared pref."); + HdmiLogger.debug("Setting '" + storageKey + "' shared pref."); mStorageAdapter.storeSharedPref(storageKey, value); notifySettingChanged(setting); } @@ -366,6 +370,9 @@ public class HdmiCecConfig { case Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP: notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE); break; + case Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED: + notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE); + break; } } @@ -399,6 +406,7 @@ public class HdmiCecConfig { Global.HDMI_CONTROL_ENABLED, Global.HDMI_CEC_VERSION, Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, + Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, }; for (String setting: settings) { resolver.registerContentObserver(Global.getUriFor(setting), false, @@ -597,7 +605,7 @@ public class HdmiCecConfig { throw new IllegalArgumentException("Setting '" + name + "' is not a string-type setting."); } - Slog.d(TAG, "Getting CEC setting value '" + name + "'."); + HdmiLogger.debug("Getting CEC setting value '" + name + "'."); return retrieveValue(setting, setting.getDefaultValue().getStringValue()); } @@ -613,7 +621,7 @@ public class HdmiCecConfig { throw new IllegalArgumentException("Setting '" + name + "' is not a int-type setting."); } - Slog.d(TAG, "Getting CEC setting value '" + name + "'."); + HdmiLogger.debug("Getting CEC setting value '" + name + "'."); String defaultValue = Integer.toString(getIntValue(setting.getDefaultValue())); String value = retrieveValue(setting, defaultValue); return Integer.parseInt(value); @@ -638,7 +646,7 @@ public class HdmiCecConfig { throw new IllegalArgumentException("Invalid CEC setting '" + name + "' value: '" + value + "'."); } - Slog.d(TAG, "Updating CEC setting '" + name + "' to '" + value + "'."); + HdmiLogger.debug("Updating CEC setting '" + name + "' to '" + value + "'."); storeValue(setting, value); } @@ -661,7 +669,7 @@ public class HdmiCecConfig { throw new IllegalArgumentException("Invalid CEC setting '" + name + "' value: '" + value + "'."); } - Slog.d(TAG, "Updating CEC setting '" + name + "' to '" + value + "'."); + HdmiLogger.debug("Updating CEC setting '" + name + "' to '" + value + "'."); storeValue(setting, Integer.toString(value)); } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 0b10cc36ded9..ccce9dc43e6d 100755 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -619,7 +619,9 @@ abstract class HdmiCecLocalDevice { } else if (mService.isPowerStandbyOrTransient() && isPowerOnOrToggleCommand(message)) { mService.wakeUp(); return true; - } else if (!mService.isHdmiCecVolumeControlEnabled() && isVolumeOrMuteCommand(message)) { + } else if (mService.getHdmiCecVolumeControl() + == HdmiControlManager.VOLUME_CONTROL_DISABLED && isVolumeOrMuteCommand( + message)) { return false; } @@ -1142,7 +1144,8 @@ abstract class HdmiCecLocalDevice { @ServiceThreadOnly protected void sendVolumeKeyEvent(int keyCode, boolean isPressed) { assertRunOnServiceThread(); - if (!mService.isHdmiCecVolumeControlEnabled()) { + if (mService.getHdmiCecVolumeControl() + == HdmiControlManager.VOLUME_CONTROL_DISABLED) { return; } if (!HdmiCecKeycode.isVolumeKeycode(keyCode)) { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java index a945c90d30ef..b909b1639e1a 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java @@ -373,7 +373,8 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { @ServiceThreadOnly protected boolean handleGiveAudioStatus(HdmiCecMessage message) { assertRunOnServiceThread(); - if (isSystemAudioControlFeatureEnabled() && mService.isHdmiCecVolumeControlEnabled()) { + if (isSystemAudioControlFeatureEnabled() && mService.getHdmiCecVolumeControl() + == HdmiControlManager.VOLUME_CONTROL_ENABLED) { reportAudioStatus(message.getSource()); } else { mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); @@ -723,7 +724,8 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { void reportAudioStatus(int source) { assertRunOnServiceThread(); - if (!mService.isHdmiCecVolumeControlEnabled()) { + if (mService.getHdmiCecVolumeControl() + == HdmiControlManager.VOLUME_CONTROL_DISABLED) { return; } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index ad3773e78b6a..e568aa228b40 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -618,7 +618,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly protected boolean handleReportAudioStatus(HdmiCecMessage message) { assertRunOnServiceThread(); - if (!mService.isHdmiCecVolumeControlEnabled()) { + if (mService.getHdmiCecVolumeControl() + == HdmiControlManager.VOLUME_CONTROL_DISABLED) { return false; } @@ -897,7 +898,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } void setAudioStatus(boolean mute, int volume) { - if (!isSystemAudioActivated() || !mService.isHdmiCecVolumeControlEnabled()) { + if (!isSystemAudioActivated() || mService.getHdmiCecVolumeControl() + == HdmiControlManager.VOLUME_CONTROL_DISABLED) { return; } synchronized (mLock) { @@ -919,7 +921,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { // On initialization process, getAvrDeviceInfo() may return null and cause exception return; } - if (delta == 0 || !isSystemAudioActivated() || !mService.isHdmiCecVolumeControlEnabled()) { + if (delta == 0 || !isSystemAudioActivated() || mService.getHdmiCecVolumeControl() + == HdmiControlManager.VOLUME_CONTROL_DISABLED) { return; } @@ -948,7 +951,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly void changeMute(boolean mute) { assertRunOnServiceThread(); - if (getAvrDeviceInfo() == null || !mService.isHdmiCecVolumeControlEnabled()) { + if (getAvrDeviceInfo() == null || mService.getHdmiCecVolumeControl() + == HdmiControlManager.VOLUME_CONTROL_DISABLED) { // On initialization process, getAvrDeviceInfo() may return null and cause exception return; } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index b427bd0a0b06..9b194ae84a37 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -18,6 +18,7 @@ package com.android.server.hdmi; import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE; import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE; +import static android.hardware.hdmi.HdmiControlManager.HDMI_CEC_CONTROL_ENABLED; import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED; import static com.android.server.hdmi.Constants.DISABLED; @@ -206,7 +207,8 @@ public class HdmiControlService extends SystemService { // Whether HDMI CEC volume control is enabled or not. @GuardedBy("mLock") - private boolean mHdmiCecVolumeControlEnabled; + @HdmiControlManager.VolumeControl + private int mHdmiCecVolumeControl; // Make sure HdmiCecConfig is instantiated and the XMLs are read. private final HdmiCecConfig mHdmiCecConfig; @@ -324,7 +326,8 @@ public class HdmiControlService extends SystemService { // Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol // handling will be disabled and no request will be handled. @GuardedBy("mLock") - private boolean mHdmiControlEnabled; + @HdmiControlManager.HdmiCecControl + private int mHdmiControlEnabled; // Set to true while the service is in normal mode. While set to false, no input change is // allowed. Used for situations where input change can confuse users such as channel auto-scan, @@ -476,10 +479,9 @@ public class HdmiControlService extends SystemService { mPowerStatusController.setPowerStatus(getInitialPowerStatus()); mProhibitMode = false; mHdmiControlEnabled = mHdmiCecConfig.getIntValue( - HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED) - == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED; - mHdmiCecVolumeControlEnabled = readBooleanSetting( - Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true); + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED); + setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE)); mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true); if (mCecController == null) { @@ -496,7 +498,7 @@ public class HdmiControlService extends SystemService { Slog.i(TAG, "Device does not support MHL-control."); } mHdmiCecNetwork = new HdmiCecNetwork(this, mCecController, mMhlController); - if (mHdmiControlEnabled) { + if (mHdmiControlEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED) { initializeCec(INITIATED_BY_BOOT_UP); } else { mCecController.setOption(OptionKey.ENABLE_CEC, false); @@ -512,9 +514,8 @@ public class HdmiControlService extends SystemService { new HdmiCecConfig.SettingChangeListener() { @Override public void onChange(String setting) { - boolean enabled = mHdmiCecConfig.getIntValue( - HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED) - == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED; + @HdmiControlManager.HdmiCecControl int enabled = mHdmiCecConfig.getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED); setControlEnabled(enabled); } }); @@ -629,7 +630,7 @@ public class HdmiControlService extends SystemService { } if (reason != -1) { invokeVendorCommandListenersOnControlStateChanged(true, reason); - announceHdmiControlStatusChange(true); + announceHdmiControlStatusChange(HDMI_CEC_CONTROL_ENABLED); } } @@ -676,7 +677,8 @@ public class HdmiControlService extends SystemService { boolean enabled = readBooleanSetting(option, true); switch (option) { case Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED: - setHdmiCecVolumeControlEnabledInternal(enabled); + setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE)); break; case Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED: if (isTvDeviceEnabled()) { @@ -1212,7 +1214,8 @@ public class HdmiControlService extends SystemService { void setAudioStatus(boolean mute, int volume) { if (!isTvDeviceEnabled() || !tv().isSystemAudioActivated() - || !isHdmiCecVolumeControlEnabled()) { + || getHdmiCecVolumeControl() + == HdmiControlManager.VOLUME_CONTROL_DISABLED) { return; } AudioManager audioManager = getAudioManager(); @@ -2120,24 +2123,6 @@ public class HdmiControlService extends SystemService { } @Override - public boolean isHdmiCecVolumeControlEnabled() { - initBinderCall(); - return HdmiControlService.this.isHdmiCecVolumeControlEnabled(); - } - - @Override - public void setHdmiCecVolumeControlEnabled(final boolean isHdmiCecVolumeControlEnabled) { - initBinderCall(); - final long token = Binder.clearCallingIdentity(); - try { - HdmiControlService.this.setHdmiCecVolumeControlEnabled( - isHdmiCecVolumeControlEnabled); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override public void reportAudioStatus(final int deviceType, final int volume, final int maxVolume, final boolean isMute) { initBinderCall(); @@ -2208,10 +2193,9 @@ public class HdmiControlService extends SystemService { // System settings pw.println("System_settings:"); pw.increaseIndent(); - pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled); pw.println("mMhlInputChangeEnabled: " + mMhlInputChangeEnabled); pw.println("mSystemAudioActivated: " + isSystemAudioActivated()); - pw.println("mHdmiCecVolumeControlEnabled: " + mHdmiCecVolumeControlEnabled); + pw.println("mHdmiCecVolumeControlEnabled: " + mHdmiCecVolumeControl); pw.decreaseIndent(); // CEC settings @@ -2340,6 +2324,13 @@ public class HdmiControlService extends SystemService { } } + @VisibleForTesting + void setHdmiCecVolumeControlEnabledInternal( + @HdmiControlManager.VolumeControl int hdmiCecVolumeControl) { + mHdmiCecVolumeControl = hdmiCecVolumeControl; + announceHdmiCecVolumeControlFeatureChange(hdmiCecVolumeControl); + } + // Get the source address to send out commands to devices connected to the current device // when other services interact with HdmiControlService. private int getRemoteControlSourceAddress() { @@ -2533,10 +2524,10 @@ public class HdmiControlService extends SystemService { // Return the current status of mHdmiCecVolumeControlEnabled; synchronized (mLock) { try { - listener.onHdmiCecVolumeControlFeature(mHdmiCecVolumeControlEnabled); + listener.onHdmiCecVolumeControlFeature(mHdmiCecVolumeControl); } catch (RemoteException e) { Slog.e(TAG, "Failed to report HdmiControlVolumeControlStatusChange: " - + mHdmiCecVolumeControlEnabled, e); + + mHdmiCecVolumeControl, e); } } } @@ -2781,7 +2772,7 @@ public class HdmiControlService extends SystemService { } } - private void announceHdmiControlStatusChange(boolean isEnabled) { + private void announceHdmiControlStatusChange(@HdmiControlManager.HdmiCecControl int isEnabled) { assertRunOnServiceThread(); synchronized (mLock) { List<IHdmiControlStatusChangeListener> listeners = new ArrayList<>( @@ -2795,16 +2786,18 @@ public class HdmiControlService extends SystemService { } private void invokeHdmiControlStatusChangeListenerLocked( - IHdmiControlStatusChangeListener listener, boolean isEnabled) { + IHdmiControlStatusChangeListener listener, + @HdmiControlManager.HdmiCecControl int isEnabled) { invokeHdmiControlStatusChangeListenerLocked(Collections.singletonList(listener), isEnabled); } private void invokeHdmiControlStatusChangeListenerLocked( - Collection<IHdmiControlStatusChangeListener> listeners, boolean isEnabled) { + Collection<IHdmiControlStatusChangeListener> listeners, + @HdmiControlManager.HdmiCecControl int isEnabled) { if (listeners.isEmpty()) { return; } - if (isEnabled) { + if (isEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED) { queryDisplayStatus(new IHdmiControlCallback.Stub() { public void onComplete(int status) { boolean isAvailable = true; @@ -2822,7 +2815,8 @@ public class HdmiControlService extends SystemService { } private void invokeHdmiControlStatusChangeListenerLocked( - Collection<IHdmiControlStatusChangeListener> listeners, boolean isEnabled, + Collection<IHdmiControlStatusChangeListener> listeners, + @HdmiControlManager.HdmiCecControl int isEnabled, boolean isCecAvailable) { for (IHdmiControlStatusChangeListener listener : listeners) { try { @@ -2835,15 +2829,16 @@ public class HdmiControlService extends SystemService { } } - private void announceHdmiCecVolumeControlFeatureChange(boolean isEnabled) { + private void announceHdmiCecVolumeControlFeatureChange( + @HdmiControlManager.VolumeControl int hdmiCecVolumeControl) { assertRunOnServiceThread(); mHdmiCecVolumeControlFeatureListenerRecords.broadcast(listener -> { try { - listener.onHdmiCecVolumeControlFeature(isEnabled); + listener.onHdmiCecVolumeControlFeature(hdmiCecVolumeControl); } catch (RemoteException e) { Slog.e(TAG, "Failed to report HdmiControlVolumeControlStatusChange: " - + isEnabled); + + hdmiCecVolumeControl); } }); } @@ -2888,7 +2883,7 @@ public class HdmiControlService extends SystemService { boolean isControlEnabled() { synchronized (mLock) { - return mHdmiControlEnabled; + return mHdmiControlEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED; } } @@ -2962,7 +2957,7 @@ public class HdmiControlService extends SystemService { mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON, false); if (mCecController != null) { - if (mHdmiControlEnabled) { + if (mHdmiControlEnabled == HDMI_CEC_CONTROL_ENABLED) { int startReason = -1; switch (wakeUpAction) { case WAKE_UP_SCREEN_ON: @@ -3193,31 +3188,10 @@ public class HdmiControlService extends SystemService { } } - void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled) { - setHdmiCecVolumeControlEnabledInternal(isHdmiCecVolumeControlEnabled); - - writeBooleanSetting(Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, - isHdmiCecVolumeControlEnabled); - } - - @VisibleForTesting - void setHdmiCecVolumeControlEnabledInternal(boolean isHdmiCecVolumeControlEnabled) { - synchronized (mLock) { - mHdmiCecVolumeControlEnabled = isHdmiCecVolumeControlEnabled; - - boolean storedValue = readBooleanSetting(Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, - true); - if (storedValue != isHdmiCecVolumeControlEnabled) { - HdmiLogger.debug("Changing HDMI CEC volume control feature state: %s", - isHdmiCecVolumeControlEnabled); - } - } - announceHdmiCecVolumeControlFeatureChange(isHdmiCecVolumeControlEnabled); - } - - boolean isHdmiCecVolumeControlEnabled() { + @HdmiControlManager.VolumeControl + int getHdmiCecVolumeControl() { synchronized (mLock) { - return mHdmiCecVolumeControlEnabled; + return mHdmiCecVolumeControl; } } @@ -3252,21 +3226,21 @@ public class HdmiControlService extends SystemService { } @ServiceThreadOnly - void setControlEnabled(boolean enabled) { + void setControlEnabled(@HdmiControlManager.HdmiCecControl int enabled) { assertRunOnServiceThread(); synchronized (mLock) { mHdmiControlEnabled = enabled; } - if (enabled) { + if (enabled == HDMI_CEC_CONTROL_ENABLED) { enableHdmiControlService(); - setHdmiCecVolumeControlEnabledInternal( - readBooleanSetting(Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true)); + setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE)); return; } - setHdmiCecVolumeControlEnabledInternal(false); + setHdmiCecVolumeControlEnabledInternal(HdmiControlManager.VOLUME_CONTROL_DISABLED); // Call the vendor handler before the service is disabled. invokeVendorCommandListenersOnControlStateChanged(false, HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING); diff --git a/services/core/java/com/android/server/hdmi/cec_config.xml b/services/core/java/com/android/server/hdmi/cec_config.xml index 480e0ec040a8..e6e3c2f5eda1 100644 --- a/services/core/java/com/android/server/hdmi/cec_config.xml +++ b/services/core/java/com/android/server/hdmi/cec_config.xml @@ -46,4 +46,13 @@ </allowed-values> <default-value int-value="1" /> </setting> + <setting name="volume_control_enabled" + value-type="int" + user-configurable="true"> + <allowed-values> + <value int-value="0" /> + <value int-value="1" /> + </allowed-values> + <default-value int-value="1" /> + </setting> </cec-settings> 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/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java index 63a42f845cee..dc1a26ae6fea 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; +import android.hardware.SensorPrivacyManager; import android.hardware.contexthub.V1_0.AsyncEventType; import android.hardware.contexthub.V1_0.ContextHub; import android.hardware.contexthub.V1_0.ContextHubMsg; @@ -117,6 +118,9 @@ public class ContextHubService extends IContextHubService.Stub { // True if WiFi is available for the Context Hub private boolean mIsWifiAvailable = false; + // True if audio is disabled for the ContextHub + private boolean mIsAudioDisabled = false; + // Lock object for sendWifiSettingUpdate() private final Object mSendWifiSettingUpdateLock = new Object(); @@ -256,6 +260,21 @@ public class ContextHubService extends IContextHubService.Stub { } }, UserHandle.USER_ALL); } + + if (mContextHubWrapper.supportsMicrophoneDisableSettingNotifications()) { + sendMicrophoneDisableSettingUpdate(); + + SensorPrivacyManager.OnSensorPrivacyChangedListener listener = + new SensorPrivacyManager.OnSensorPrivacyChangedListener() { + @Override + public void onSensorPrivacyChanged(boolean enabled) { + sendMicrophoneDisableSettingUpdate(); + } + }; + SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext); + manager.addSensorPrivacyListener( + SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE, listener); + } } /** @@ -999,6 +1018,21 @@ public class ContextHubService extends IContextHubService.Stub { mContextHubWrapper.onAirplaneModeSettingChanged(enabled); } + /** + * Obtains the latest microphone disable setting value and notifies the + * Context Hub. + */ + private void sendMicrophoneDisableSettingUpdate() { + SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext); + boolean disabled = manager.isIndividualSensorPrivacyEnabled( + SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE); + if (mIsAudioDisabled != disabled) { + mIsAudioDisabled = disabled; + mContextHubWrapper.onMicrophoneDisableSettingChanged(disabled); + } + } + + private String getCallingPackageName() { return mContext.getPackageManager().getNameForUid(Binder.getCallingUid()); } diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java index 4242d72239e2..c11e289c116b 100644 --- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java +++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java @@ -129,6 +129,18 @@ public abstract class IContextHubWrapper { */ public abstract void onAirplaneModeSettingChanged(boolean enabled); + /** + * @return True if this version of the Contexthub HAL supports microphone + * disable setting notifications. + */ + public abstract boolean supportsMicrophoneDisableSettingNotifications(); + + /** + * Notifies the Contexthub implementation of a microphone disable setting + * change. + */ + public abstract void onMicrophoneDisableSettingChanged(boolean enabled); + private static class ContextHubWrapperV1_0 extends IContextHubWrapper { private android.hardware.contexthub.V1_0.IContexthub mHub; @@ -152,6 +164,10 @@ public abstract class IContextHubWrapper { return false; } + public boolean supportsMicrophoneDisableSettingNotifications() { + return false; + } + public void onLocationSettingChanged(boolean enabled) { } @@ -160,6 +176,9 @@ public abstract class IContextHubWrapper { public void onAirplaneModeSettingChanged(boolean enabled) { } + + public void onMicrophoneDisableSettingChanged(boolean enabled) { + } } private static class ContextHubWrapperV1_1 extends IContextHubWrapper { @@ -185,6 +204,10 @@ public abstract class IContextHubWrapper { return false; } + public boolean supportsMicrophoneDisableSettingNotifications() { + return false; + } + public void onLocationSettingChanged(boolean enabled) { try { mHub.onSettingChanged(Setting.LOCATION, @@ -199,6 +222,9 @@ public abstract class IContextHubWrapper { public void onAirplaneModeSettingChanged(boolean enabled) { } + + public void onMicrophoneDisableSettingChanged(boolean enabled) { + } } private static class ContextHubWrapperV1_2 extends IContextHubWrapper { @@ -224,6 +250,10 @@ public abstract class IContextHubWrapper { return true; } + public boolean supportsMicrophoneDisableSettingNotifications() { + return true; + } + public void onLocationSettingChanged(boolean enabled) { sendSettingChanged(Setting.LOCATION, enabled ? SettingValue.ENABLED : SettingValue.DISABLED); @@ -239,6 +269,11 @@ public abstract class IContextHubWrapper { enabled ? SettingValue.ENABLED : SettingValue.DISABLED); } + public void onMicrophoneDisableSettingChanged(boolean enabled) { + sendSettingChanged(android.hardware.contexthub.V1_2.Setting.GLOBAL_MIC_DISABLE, + enabled ? SettingValue.ENABLED : SettingValue.DISABLED); + } + private void sendSettingChanged(byte setting, byte newValue) { try { mHub.onSettingChanged_1_2(setting, newValue); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java index 5d0544b33cd7..7dd961a6e4e4 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java @@ -753,8 +753,9 @@ class LockSettingsStorage { pw.increaseIndent(); File[] files = userPath.listFiles(); if (files != null) { + Arrays.sort(files); for (File file : files) { - pw.println(String.format("%4d %s %s", file.length(), + pw.println(String.format("%6d %s %s", file.length(), LockSettingsService.timestampToString(file.lastModified()), file.getName())); } diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java index 289290bab4dc..fbec91576ca1 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java @@ -224,6 +224,10 @@ class RebootEscrowManager { for (UserInfo user : rebootEscrowUsers) { allUsersUnlocked &= restoreRebootEscrowForUser(user.id, escrowKey, kk); } + + // Clear the old key in keystore. A new key will be generated by new RoR requests. + mKeyStoreManager.clearKeyStoreEncryptionKey(); + onEscrowRestoreComplete(allUsersUnlocked); } @@ -273,9 +277,6 @@ class RebootEscrowManager { } catch (IOException e) { Slog.w(TAG, "Could not load reboot escrow data for user " + userId, e); return false; - } finally { - // Clear the old key in keystore. A new key will be generated by new RoR requests. - mKeyStoreManager.clearKeyStoreEncryptionKey(); } } 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 3e40b2756b10..5fa799839789 100644 --- a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java +++ b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java @@ -18,8 +18,12 @@ package com.android.server.media.metrics; import android.content.Context; 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; @@ -53,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 } @@ -78,5 +109,42 @@ public final class PlaybackMetricsManagerService extends SystemService { .build(); StatsLog.write(statsEvent); } + + public void reportNetworkEvent( + String sessionId, NetworkEvent event, int userId) { + StatsEvent statsEvent = StatsEvent.newBuilder() + .setAtomId(321) + .writeString(sessionId) + .writeInt(event.getType()) + .writeLong(event.getTimeSincePlaybackCreatedMillis()) + .usePooledBuffer() + .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/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 61b218ce7196..60d83f1fd4e8 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -3085,8 +3085,23 @@ public class NotificationManagerService extends SystemService { synchronized (mToastQueue) { int uid = Binder.getCallingUid(); + int userId = UserHandle.getUserId(uid); if (enable) { mToastRateLimitingDisabledUids.remove(uid); + try { + String[] packages = mPackageManager.getPackagesForUid(uid); + if (packages == null) { + Slog.e(TAG, "setToastRateLimitingEnabled method haven't found any " + + "packages for the given uid: " + uid + ", toast rate " + + "limiter not reset for that uid."); + return; + } + for (String pkg : packages) { + mToastRateLimiter.clear(userId, pkg); + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to reset toast rate limiter for given uid", e); + } } else { mToastRateLimitingDisabledUids.add(uid); } 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 a1dfb65387fa..7952c255fd57 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/MultiRateLimiter.java b/services/core/java/com/android/server/utils/quota/MultiRateLimiter.java index fdbe4b425d39..82c6d01056fd 100644 --- a/services/core/java/com/android/server/utils/quota/MultiRateLimiter.java +++ b/services/core/java/com/android/server/utils/quota/MultiRateLimiter.java @@ -19,6 +19,8 @@ package com.android.server.utils.quota; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.pm.PackageManager; +import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -48,15 +50,18 @@ import java.util.List; * @hide */ public class MultiRateLimiter { + private static final String TAG = "MultiRateLimiter"; private static final CountQuotaTracker[] EMPTY_TRACKER_ARRAY = {}; private final Object mLock = new Object(); @GuardedBy("mLock") private final CountQuotaTracker[] mQuotaTrackers; + private final PackageManager mPackageManager; - private MultiRateLimiter(List<CountQuotaTracker> quotaTrackers) { + private MultiRateLimiter(List<CountQuotaTracker> quotaTrackers, PackageManager packageManager) { mQuotaTrackers = quotaTrackers.toArray(EMPTY_TRACKER_ARRAY); + mPackageManager = packageManager; } /** Record that an event happened and count it towards the given quota. */ @@ -73,6 +78,13 @@ public class MultiRateLimiter { } } + /** Remove all saved events from the rate limiter for the given app (reset it). */ + public void clear(int userId, @NonNull String packageName) { + synchronized (mLock) { + clearLocked(userId, packageName); + } + } + @GuardedBy("mLock") private void noteEventLocked(int userId, @NonNull String packageName, @Nullable String tag) { for (CountQuotaTracker quotaTracker : mQuotaTrackers) { @@ -91,6 +103,22 @@ public class MultiRateLimiter { return true; } + @GuardedBy("mLock") + private void clearLocked(int userId, @NonNull String packageName) { + try { + int uid = mPackageManager.getApplicationInfoAsUser(packageName, 0, userId).uid; + for (CountQuotaTracker quotaTracker : mQuotaTrackers) { + // This method behaves as if the package has been removed from the device, which + // isn't the case here, but it does similar clean-up to what we are aiming for here, + // so it works for this use case. + quotaTracker.onAppRemovedLocked(packageName, uid); + } + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "clear(userId, packageName) called with unrecognized arguments, no " + + "action taken"); + } + } + /** Can create a new {@link MultiRateLimiter}. */ public static class Builder { @@ -154,7 +182,7 @@ public class MultiRateLimiter { * limit. */ public MultiRateLimiter build() { - return new MultiRateLimiter(mQuotaTrackers); + return new MultiRateLimiter(mQuotaTrackers, mContext.getPackageManager()); } } 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/utils/quota/QuotaTracker.java b/services/core/java/com/android/server/utils/quota/QuotaTracker.java index 7f446185f63f..673862cc18e5 100644 --- a/services/core/java/com/android/server/utils/quota/QuotaTracker.java +++ b/services/core/java/com/android/server/utils/quota/QuotaTracker.java @@ -58,7 +58,7 @@ import java.util.PriorityQueue; * of quota until it is below that limit again. Limits are applied according to the category * the UPTC is placed in. Categories are basic constructs to apply different limits to * different groups of UPTCs. For example, standby buckets can be a set of categories, or - * foreground & background could be two categories. If every UPTC should have the limits + * foreground & background could be two categories. If every UPTC should have the same limits * applied, then only one category is needed. * * Note: all limits are enforced per category unless explicitly stated otherwise. @@ -361,7 +361,7 @@ abstract class QuotaTracker { abstract void handleRemovedAppLocked(String packageName, int uid); @GuardedBy("mLock") - private void onAppRemovedLocked(String packageName, int uid) { + void onAppRemovedLocked(String packageName, int uid) { if (packageName == null) { Slog.wtf(TAG, "Told app removed but given null package name."); return; 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/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java index a34a50737ec5..536375f1311d 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -36,6 +36,7 @@ import android.provider.Settings; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import java.util.ArrayList; @@ -59,7 +60,8 @@ public final class VibrationSettings { private final Vibrator mVibrator; private final AudioManager mAudioManager; private final SettingsObserver mSettingObserver; - private final UidObserver mUidObserver; + @VisibleForTesting + final UidObserver mUidObserver; @GuardedBy("mLock") private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>(); @@ -251,8 +253,7 @@ public final class VibrationSettings { * allowed to play in the background (i.e. it's a notification, ringtone or alarm vibration). */ public boolean shouldVibrateForUid(int uid, int usageHint) { - return mUidObserver.isUidForeground(uid) || isNotification(usageHint) - || isRingtone(usageHint) || isAlarm(usageHint); + return mUidObserver.isUidForeground(uid) || isClassAlarm(usageHint); } /** @@ -292,6 +293,11 @@ public final class VibrationSettings { return usageHint == VibrationAttributes.USAGE_ALARM; } + private static boolean isClassAlarm(int usageHint) { + return (usageHint & VibrationAttributes.USAGE_CLASS_MASK) + == VibrationAttributes.USAGE_CLASS_ALARM; + } + /** Updates all vibration settings and triggers registered listeners. */ public void updateSettings() { synchronized (mLock) { @@ -414,7 +420,8 @@ public final class VibrationSettings { } /** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */ - private final class UidObserver extends IUidObserver.Stub { + @VisibleForTesting + final class UidObserver extends IUidObserver.Stub { private final SparseArray<Integer> mProcStatesCache = new SparseArray<>(); public boolean isUidForeground(int uid) { 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 a9c54741fcf6..0f4bb4c5b081 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); } @@ -2601,7 +2606,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A finishActivityResults(resultCode, resultData, resultGrants); - final boolean endTask = task.getActivityBelow(this) == null + final boolean endTask = task.getTopNonFinishingActivity() == null && !task.isClearingToReuseTask(); final int transit = endTask ? TRANSIT_OLD_TASK_CLOSE : TRANSIT_OLD_ACTIVITY_CLOSE; if (isState(RESUMED)) { @@ -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; } /** @@ -6569,7 +6588,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } return !isResizeable() && (info.isFixedOrientation() || info.hasFixedAspectRatio()) // The configuration of non-standard type should be enforced by system. - && isActivityTypeStandard() + // {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} is set when this activity is + // added to a task, but this function is called when resolving the launch params, at + // which point, the activity type is still undefined if it will be standard. + // For other non-standard types, the type is set in the constructor, so this should + // not be a problem. + && isActivityTypeStandardOrUndefined() && !mAtmService.mForceResizableActivities; } @@ -7798,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. @@ -7831,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(); @@ -7864,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]); } } @@ -7881,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/Session.java b/services/core/java/com/android/server/wm/Session.java index ff2509b9ffc7..b1606c506d5b 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -22,6 +22,7 @@ import static android.Manifest.permission.HIDE_OVERLAY_WINDOWS; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY; +import static android.Manifest.permission.USE_BACKGROUND_BLUR; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY; import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT; @@ -108,6 +109,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { final boolean mCanCreateSystemApplicationOverlay; final boolean mCanHideNonSystemOverlayWindows; final boolean mCanAcquireSleepToken; + final boolean mCanUseBackgroundBlur; private AlertWindowNotification mAlertWindowNotification; private boolean mShowingAlertWindowNotificationAllowed; private boolean mClientDead = false; @@ -136,6 +138,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { && !mService.mAtmInternal.isCallerRecents(mUid); mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER) == PERMISSION_GRANTED; + mCanUseBackgroundBlur = service.mContext.checkCallingOrSelfPermission(USE_BACKGROUND_BLUR) + == PERMISSION_GRANTED; mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications; mDragDropController = mService.mDragDropController; StringBuilder sb = new StringBuilder(); 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/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index a034bac993e0..4156ed602464 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1620,7 +1620,7 @@ public class WindowManagerService extends IWindowManager.Stub final WindowState win = new WindowState(this, session, client, token, parentWindow, appOp[0], attrs, viewVisibility, session.mUid, userId, - session.mCanAddInternalSystemWindow); + session.mCanAddInternalSystemWindow, session.mCanUseBackgroundBlur); if (win.mDeathRecipient == null) { // Client has apparently died, so there is no reason to // continue. 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..b7602fec73df 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -45,6 +45,7 @@ import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; +import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; @@ -213,6 +214,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; @@ -296,6 +298,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final int mShowUserId; /** The owner has {@link android.Manifest.permission#INTERNAL_SYSTEM_WINDOW} */ final boolean mOwnerCanAddInternalSystemWindow; + /** The owner has {@link android.Manifest.permission#USE_BACKGROUND_BLUR} */ + final boolean mOwnerCanUseBackgroundBlur; final WindowId mWindowId; WindowToken mToken; // The same object as mToken if this is an app window and null for non-app windows. @@ -645,9 +649,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 @@ -848,9 +857,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token, WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility, - int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow) { + int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow, + boolean ownerCanUseBackgroundBlur) { this(service, s, c, token, parentWindow, appOp, a, viewVisibility, ownerId, showUserId, - ownerCanAddInternalSystemWindow, new PowerManagerWrapper() { + ownerCanAddInternalSystemWindow, ownerCanUseBackgroundBlur, + new PowerManagerWrapper() { @Override public void wakeUp(long time, @WakeReason int reason, String details) { service.mPowerManager.wakeUp(time, reason, details); @@ -866,7 +877,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token, WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility, int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow, - PowerManagerWrapper powerManagerWrapper) { + boolean ownerCanUseBackgroundBlur, PowerManagerWrapper powerManagerWrapper) { super(service); mTmpTransaction = service.mTransactionFactory.get(); mSession = s; @@ -877,6 +888,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mOwnerUid = ownerId; mShowUserId = showUserId; mOwnerCanAddInternalSystemWindow = ownerCanAddInternalSystemWindow; + mOwnerCanUseBackgroundBlur = ownerCanUseBackgroundBlur; mWindowId = new WindowId(this); mAttrs.copyFrom(a); mLastSurfaceInsets.set(mAttrs.surfaceInsets); @@ -2097,7 +2109,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(); @@ -5239,7 +5251,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (!mAnimatingExit && mAppDied) { mIsDimming = true; getDimmer().dimAbove(getSyncTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW); - } else if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || mAttrs.backgroundBlurRadius != 0) + } else if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || isBlurEnabled()) && isVisibleNow() && !mHidden) { // Only show the Dimmer when the following is satisfied: // 1. The window has the flag FLAG_DIM_BEHIND or background blur is requested @@ -5248,11 +5260,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // 4. The WS is not hidden. mIsDimming = true; final float dimAmount = (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? mAttrs.dimAmount : 0; - getDimmer().dimBelow( - getSyncTransaction(), this, mAttrs.dimAmount, mAttrs.backgroundBlurRadius); + final int blurRadius = isBlurEnabled() ? mAttrs.backgroundBlurRadius : 0; + getDimmer().dimBelow(getSyncTransaction(), this, dimAmount, blurRadius); } } + private boolean isBlurEnabled() { + return (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 && mOwnerCanUseBackgroundBlur; + } + /** * Notifies SF about the priority of the window, if it changed. SF then uses this information 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 4fe275250ddf..f892c1f6a28e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -378,6 +378,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final String CREDENTIAL_MANAGEMENT_APP = "credentialManagementApp"; private static final String NOT_CREDENTIAL_MANAGEMENT_APP = "notCredentialManagementApp"; + private static final String NULL_STRING_ARRAY = "nullStringArray"; + // Comprehensive list of delegations. private static final String DELEGATIONS[] = { DELEGATION_CERT_INSTALL, @@ -3383,6 +3385,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()) { @@ -4186,21 +4189,36 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * Calculates strictest (maximum) value for a given password property enforced by admin[s]. */ @Override - public PasswordMetrics getPasswordMinimumMetrics(@UserIdInt int userHandle) { + public PasswordMetrics getPasswordMinimumMetrics(@UserIdInt int userHandle, + boolean deviceWideOnly) { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); - return getPasswordMinimumMetricsUnchecked(userHandle); + return getPasswordMinimumMetricsUnchecked(userHandle, deviceWideOnly); } private PasswordMetrics getPasswordMinimumMetricsUnchecked(@UserIdInt int userId) { + return getPasswordMinimumMetricsUnchecked(userId, false); + } + + private PasswordMetrics getPasswordMinimumMetricsUnchecked(@UserIdInt int userId, + boolean deviceWideOnly) { if (!mHasFeature) { new PasswordMetrics(CREDENTIAL_TYPE_NONE); } Preconditions.checkArgumentNonnegative(userId, "Invalid userId"); + if (deviceWideOnly) { + Preconditions.checkArgument(!isManagedProfile(userId)); + } ArrayList<PasswordMetrics> adminMetrics = new ArrayList<>(); synchronized (getLockObject()) { - List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userId); + final List<ActiveAdmin> admins; + if (deviceWideOnly) { + admins = getActiveAdminsForUserAndItsManagedProfilesLocked(userId, + /* shouldIncludeProfileAdmins */ (user) -> false); + } else { + admins = getActiveAdminsForLockscreenPoliciesLocked(userId); + } for (ActiveAdmin admin : admins) { final boolean isAdminOfUser = userId == admin.getUserHandle().getIdentifier(); // Use the password metrics from the admin in one of three cases: @@ -4260,22 +4278,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { enforceUserUnlocked(parentUser); synchronized (getLockObject()) { - - // Combine password policies across the user and its profiles. Profile admins are - // excluded since we only want explicit password requirements, while profile admin - // requirement are applicable only when the profile has unified challenge. - List<ActiveAdmin> admins = getActiveAdminsForUserAndItsManagedProfilesLocked(parentUser, - /* shouldIncludeProfileAdmins */ (user) -> false); - ArrayList<PasswordMetrics> adminMetrics = new ArrayList<>(admins.size()); - int maxRequiredComplexity = PASSWORD_COMPLEXITY_NONE; - for (ActiveAdmin admin : admins) { - adminMetrics.add(admin.mPasswordPolicy.getMinMetrics()); - maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity); - } + int complexity = getAggregatedPasswordComplexityLocked(parentUser, true); + PasswordMetrics minMetrics = getPasswordMinimumMetricsUnchecked(parentUser, true); PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(parentUser); - return PasswordMetrics.validatePasswordMetrics(PasswordMetrics.merge(adminMetrics), - maxRequiredComplexity, false, metrics).isEmpty(); + final List<PasswordValidationError> passwordValidationErrors = + PasswordMetrics.validatePasswordMetrics( + minMetrics, complexity, false, metrics); + return passwordValidationErrors.isEmpty(); } } @@ -4379,7 +4389,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { */ private boolean isPasswordSufficientForUserWithoutCheckpointLocked( @NonNull PasswordMetrics metrics, @UserIdInt int userId) { - final int complexity = getEffectivePasswordComplexityRequirementLocked(userId); + final int complexity = getAggregatedPasswordComplexityLocked(userId); PasswordMetrics minMetrics = getPasswordMinimumMetricsUnchecked(userId); final List<PasswordValidationError> passwordValidationErrors = PasswordMetrics.validatePasswordMetrics( @@ -4481,9 +4491,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private int getEffectivePasswordComplexityRequirementLocked(@UserIdInt int userHandle) { + private int getAggregatedPasswordComplexityLocked(@UserIdInt int userHandle) { + return getAggregatedPasswordComplexityLocked(userHandle, false); + } + + private int getAggregatedPasswordComplexityLocked(@UserIdInt int userHandle, + boolean deviceWideOnly) { ensureLocked(); - List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle); + final List<ActiveAdmin> admins; + if (deviceWideOnly) { + admins = getActiveAdminsForUserAndItsManagedProfilesLocked(userHandle, + /* shouldIncludeProfileAdmins */ (user) -> false); + } else { + admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle); + } int maxRequiredComplexity = PASSWORD_COMPLEXITY_NONE; for (ActiveAdmin admin : admins) { maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity); @@ -4511,7 +4532,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public int getAggregatedPasswordComplexityForUser(int userId) { + public int getAggregatedPasswordComplexityForUser(int userId, boolean deviceWideOnly) { if (!mHasFeature) { return PASSWORD_COMPLEXITY_NONE; } @@ -4520,7 +4541,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId)); synchronized (getLockObject()) { - return getEffectivePasswordComplexityRequirementLocked(userId); + return getAggregatedPasswordComplexityLocked(userId, deviceWideOnly); } } @@ -4715,7 +4736,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { final PasswordMetrics minMetrics = getPasswordMinimumMetricsUnchecked(userHandle); final List<PasswordValidationError> validationErrors; - final int complexity = getEffectivePasswordComplexityRequirementLocked(userHandle); + final int complexity = getAggregatedPasswordComplexityLocked(userHandle); // TODO: Consider changing validation API to take LockscreenCredential. if (password.isEmpty()) { validationErrors = PasswordMetrics.validatePasswordMetrics( @@ -5143,6 +5164,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 +5196,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 +5226,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 +5284,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 +6182,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 +6526,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 +7425,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 +7535,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 +8670,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 +9281,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(); @@ -9553,59 +9579,86 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public boolean setPermittedInputMethods(ComponentName who, List packageList) { + public boolean setPermittedInputMethods(ComponentName who, List packageList, + boolean calledOnParentInstance) { if (!mHasFeature) { return false; } Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + final int userId = getProfileParentUserIfRequested( + caller.getUserId(), calledOnParentInstance); + if (calledOnParentInstance) { + Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller)); + Preconditions.checkArgument(packageList == null || packageList.isEmpty(), + "Permitted input methods must allow all input methods or only " + + "system input methods when called on the parent instance of an " + + "organization-owned device"); + } else { + Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + } if (packageList != null) { - List<InputMethodInfo> enabledImes = InputMethodManagerInternal.get() - .getEnabledInputMethodListAsUser(caller.getUserId()); + List<InputMethodInfo> enabledImes = mInjector.binderWithCleanCallingIdentity(() -> + InputMethodManagerInternal.get().getEnabledInputMethodListAsUser(userId)); if (enabledImes != null) { List<String> enabledPackages = new ArrayList<String>(); for (InputMethodInfo ime : enabledImes) { enabledPackages.add(ime.getPackageName()); } if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList, - caller.getUserId())) { - Slog.e(LOG_TAG, "Cannot set permitted input methods, " - + "because it contains already enabled input method."); + userId)) { + Slog.e(LOG_TAG, "Cannot set permitted input methods, because the list of " + + "permitted input methods excludes an already-enabled input method."); return false; } } } synchronized (getLockObject()) { - ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller); + final ActiveAdmin admin = getParentOfAdminIfRequired( + getProfileOwnerOrDeviceOwnerLocked(caller), calledOnParentInstance); admin.permittedInputMethods = packageList; saveSettingsLocked(caller.getUserId()); } - final String[] packageArray = - packageList != null ? ((List<String>) packageList).toArray(new String[0]) : null; + DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_PERMITTED_INPUT_METHODS) .setAdmin(who) - .setStrings(packageArray) + .setStrings(getStringArrayForLogging(packageList, calledOnParentInstance)) .write(); return true; } + private String[] getStringArrayForLogging(List list, boolean calledOnParentInstance) { + List<String> stringList = new ArrayList<String>(); + stringList.add(calledOnParentInstance ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT); + if (list == null) { + stringList.add(NULL_STRING_ARRAY); + } else { + stringList.addAll((List<String>) list); + } + return stringList.toArray(new String[0]); + } + @Override - public List getPermittedInputMethods(ComponentName who) { + public List getPermittedInputMethods(ComponentName who, boolean calledOnParentInstance) { if (!mHasFeature) { return null; } Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + if (calledOnParentInstance) { + Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller)); + } else { + Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + } synchronized (getLockObject()) { - ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller); + final ActiveAdmin admin = getParentOfAdminIfRequired( + getProfileOwnerOrDeviceOwnerLocked(caller), calledOnParentInstance); return admin.permittedInputMethods; } } @@ -9618,9 +9671,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { List<String> result = null; // Only device or profile owners can have permitted lists set. - DevicePolicyData policy = getUserDataUnchecked(caller.getUserId()); - for (int i = 0; i < policy.mAdminList.size(); i++) { - ActiveAdmin admin = policy.mAdminList.get(i); + List<ActiveAdmin> admins = getActiveAdminsForAffectedUserLocked(caller.getUserId()); + for (ActiveAdmin admin: admins) { List<String> fromAdmin = admin.permittedInputMethods; if (fromAdmin != null) { if (result == null) { @@ -9651,7 +9703,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isInputMethodPermittedByAdmin(ComponentName who, String packageName, - int userHandle) { + int userHandle, boolean calledOnParentInstance) { if (!mHasFeature) { return true; } @@ -9660,7 +9712,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { enforceSystemCaller("query if an input method is disabled by admin"); synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); + ActiveAdmin admin = getParentOfAdminIfRequired( + getActiveAdminUncheckedLocked(who, userHandle), calledOnParentInstance); if (admin == null) { return false; } @@ -9854,8 +9907,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final long id = mInjector.binderClearCallingIdentity(); try { - manageUserUnchecked(admin, profileOwner, userHandle, adminExtras, - /* showDisclaimer= */ true); + if (!mInjector.userManagerIsHeadlessSystemUserMode()) { + manageUserUnchecked(admin, profileOwner, userHandle, adminExtras, + /* showDisclaimer= */ true); + } else if (VERBOSE_LOG) { + Slog.v(LOG_TAG, "createAndManageUser(): skipping manageUserUnchecked() on user " + + userHandle + " on headless system user as it will be called by " + + "handleNewUserCreated()"); + } if ((flags & DevicePolicyManager.SKIP_SETUP_WIZARD) != 0) { Settings.Secure.putIntForUser(mContext.getContentResolver(), @@ -9911,12 +9970,24 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private void handleNewUserCreated(UserInfo user) { - if (!mOwners.hasDeviceOwner()) return; + if (VERBOSE_LOG) Slog.v(LOG_TAG, "handleNewUserCreated(): " + user.toFullString()); + + if (!mOwners.hasDeviceOwner() || !user.isFull() || user.isManagedProfile()) return; final int userId = user.id; - Log.i(LOG_TAG, "User " + userId + " added on DO mode; setting ShowNewUserDisclaimer"); - setShowNewUserDisclaimer(userId, DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED); + // TODO(b/177547285): add CTS test + if (mInjector.userManagerIsHeadlessSystemUserMode()) { + ComponentName admin = mOwners.getDeviceOwnerComponent(); + Slog.i(LOG_TAG, "Automatically setting profile owner (" + admin + ") on new user " + + userId); + manageUserUnchecked(/* deviceOwner= */ admin, /* profileOwner= */ admin, + /* managedUser= */ userId, /* adminExtras= */ null, + /* showDisclaimer= */ true); + } else { + Log.i(LOG_TAG, "User " + userId + " added on DO mode; setting ShowNewUserDisclaimer"); + setShowNewUserDisclaimer(userId, DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED); + } } @Override @@ -11413,6 +11484,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); @@ -12389,6 +12461,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()); @@ -12424,6 +12497,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(); @@ -14400,6 +14474,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 { @@ -14431,6 +14506,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(); @@ -14798,6 +14874,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); } @@ -14899,6 +14977,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: @@ -14971,6 +15050,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) @@ -15835,8 +15915,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Log.i(LOG_TAG, String.format("Setting Enterprise ID to %s for user %d", organizationId, userId)); + final String ownerPackage; synchronized (getLockObject()) { - ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId); + final ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId); // As the caller is the system, it must specify the component name of the profile owner // as a safety check. Preconditions.checkCallAuthorization( @@ -15844,6 +15925,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { String.format("The Profile Owner or Device Owner may only set the Enterprise ID" + " on its own user, called on user %d but owner user is %d", userId, owner.getUserHandle().getIdentifier())); + ownerPackage = owner.info.getPackageName(); Preconditions.checkState( TextUtils.isEmpty(owner.mOrganizationId) || owner.mOrganizationId.equals( organizationId), @@ -15861,5 +15943,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { saveSettingsLocked(userId); }); } + + DevicePolicyEventLogger + .createEvent(DevicePolicyEnums.SET_ORGANIZATION_ID) + .setAdmin(ownerPackage) + .setBoolean(isManagedProfile(userId)) + .write(); } } 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/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java index 16b9165915e2..091e688743dd 100644 --- a/services/people/java/com/android/server/people/PeopleService.java +++ b/services/people/java/com/android/server/people/PeopleService.java @@ -188,6 +188,9 @@ public class PeopleService extends SystemService { ConversationStatus status) { handleIncomingUser(userId); checkCallerIsSameApp(packageName); + if (status.getStartTimeMillis() > System.currentTimeMillis()) { + throw new IllegalArgumentException("Start time must be in the past"); + } mDataManager.addOrUpdateStatus(packageName, userId, conversationId, status); } 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/ConversationStatusExpirationBroadcastReceiver.java b/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java new file mode 100644 index 000000000000..c631026f4389 --- /dev/null +++ b/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.people.data; + +import android.annotation.UserIdInt; +import android.app.ActivityManager; +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.app.job.JobInfo; +import android.app.job.JobParameters; +import android.app.job.JobScheduler; +import android.app.job.JobService; +import android.app.people.ConversationStatus; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.Uri; +import android.os.CancellationSignal; +import android.os.SystemClock; + +import com.android.server.LocalServices; +import com.android.server.notification.NotificationRecord; +import com.android.server.people.PeopleServiceInternal; + +import java.util.concurrent.TimeUnit; + +/** + * If a {@link ConversationStatus} is added to the system with an expiration time, remove that + * status at that time + */ +public class ConversationStatusExpirationBroadcastReceiver extends BroadcastReceiver { + + static final String ACTION = "ConversationStatusExpiration"; + static final String EXTRA_USER_ID = "userId"; + static final int REQUEST_CODE = 10; + static final String SCHEME = "expStatus"; + + void scheduleExpiration(Context context, @UserIdInt int userId, String pkg, + String conversationId, ConversationStatus status) { + + final PendingIntent pi = PendingIntent.getBroadcast(context, + REQUEST_CODE, + new Intent(ACTION) + .setData(new Uri.Builder().scheme(SCHEME) + .appendPath(getKey(userId, pkg, conversationId, status)) + .build()) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND) + .putExtra(EXTRA_USER_ID, userId), + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + context.getSystemService(AlarmManager.class).setExactAndAllowWhileIdle( + AlarmManager.RTC_WAKEUP, status.getEndTimeMillis(), pi); + } + + private static String getKey(@UserIdInt int userId, String pkg, + String conversationId, ConversationStatus status) { + return userId + pkg + conversationId + status.getId(); + } + + static IntentFilter getFilter() { + IntentFilter conversationStatusFilter = + new IntentFilter(ConversationStatusExpirationBroadcastReceiver.ACTION); + conversationStatusFilter.addDataScheme( + ConversationStatusExpirationBroadcastReceiver.SCHEME); + return conversationStatusFilter; + } + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action == null) { + return; + } + if (ACTION.equals(action)) { + new Thread(() -> { + PeopleServiceInternal peopleServiceInternal = + LocalServices.getService(PeopleServiceInternal.class); + peopleServiceInternal.pruneDataForUser(intent.getIntExtra(EXTRA_USER_ID, + ActivityManager.getCurrentUser()), new CancellationSignal()); + }).start(); + } + } +} 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/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java index e04e287d80f6..752141598c9d 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -124,6 +124,7 @@ public class DataManager { private PackageManagerInternal mPackageManagerInternal; private NotificationManagerInternal mNotificationManagerInternal; private UserManager mUserManager; + private ConversationStatusExpirationBroadcastReceiver mStatusExpReceiver; public DataManager(Context context) { this(context, new Injector()); @@ -145,6 +146,10 @@ public class DataManager { mShortcutServiceInternal.addShortcutChangeCallback(new ShortcutServiceCallback()); + mStatusExpReceiver = new ConversationStatusExpirationBroadcastReceiver(); + mContext.registerReceiver(mStatusExpReceiver, + ConversationStatusExpirationBroadcastReceiver.getFilter()); + IntentFilter shutdownIntentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN); BroadcastReceiver shutdownBroadcastReceiver = new ShutdownBroadcastReceiver(); mContext.registerReceiver(shutdownBroadcastReceiver, shutdownIntentFilter); @@ -296,6 +301,27 @@ public class DataManager { } /** + * Removes any status with an expiration time in the past. + */ + public void pruneExpiredConversationStatuses(@UserIdInt int callingUserId, long currentTimeMs) { + forPackagesInProfile(callingUserId, packageData -> { + final ConversationStore cs = packageData.getConversationStore(); + packageData.forAllConversations(conversationInfo -> { + ConversationInfo.Builder builder = new ConversationInfo.Builder(conversationInfo); + List<ConversationStatus> newStatuses = new ArrayList<>(); + for (ConversationStatus status : conversationInfo.getStatuses()) { + if (status.getEndTimeMillis() < 0 + || currentTimeMs < status.getEndTimeMillis()) { + newStatuses.add(status); + } + } + builder.setStatuses(newStatuses); + cs.addOrUpdate(builder.build()); + }); + }); + } + + /** * Returns the last notification interaction with the specified conversation. If the * conversation can't be found or no interactions have been recorded, returns 0L. */ @@ -317,6 +343,12 @@ public class DataManager { ConversationInfo.Builder builder = new ConversationInfo.Builder(convToModify); builder.addOrUpdateStatus(status); cs.addOrUpdate(builder.build()); + + if (status.getEndTimeMillis() >= 0) { + mStatusExpReceiver.scheduleExpiration( + mContext, userId, packageName, conversationId, status); + } + } public void clearStatus(String packageName, int userId, String conversationId, @@ -458,6 +490,7 @@ public class DataManager { packageData.getEventStore().deleteEventHistories(EventStore.CATEGORY_SMS); } packageData.pruneOrphanEvents(); + pruneExpiredConversationStatuses(userId, System.currentTimeMillis()); pruneOldRecentConversations(userId, System.currentTimeMillis()); cleanupCachedShortcuts(userId, MAX_CACHED_RECENT_SHORTCUTS); }); 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/MultiRateLimiterTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/quota/MultiRateLimiterTest.java index df533f3c122a..ab86e19c4a56 100644 --- a/services/tests/mockingservicestests/src/com/android/server/utils/quota/MultiRateLimiterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/utils/quota/MultiRateLimiterTest.java @@ -16,15 +16,29 @@ package com.android.server.utils.quota; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + +import android.content.ContextWrapper; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.UserHandle; import android.testing.TestableContext; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; +import org.junit.After; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; import java.time.Duration; @@ -32,15 +46,20 @@ import java.time.Duration; public class MultiRateLimiterTest { private static final int USER_ID = 1; + private static final int UID_1 = 10_001; private static final String PACKAGE_NAME_1 = "com.android.package.one"; private static final String PACKAGE_NAME_2 = "com.android.package.two"; private static final String TAG = "tag"; @Rule - public final TestableContext mContext = + public final TestableContext mTestableContext = new TestableContext(InstrumentationRegistry.getContext(), null); private final InjectorForTest mInjector = new InjectorForTest(); + private MockitoSession mMockingSession; + private ContextWrapper mContext; + + @Mock private PackageManager mPackageManager; private static class InjectorForTest extends QuotaTracker.Injector { Duration mElapsedTime = Duration.ZERO; @@ -56,6 +75,35 @@ public class MultiRateLimiterTest { } } + @Before + public void setup() throws Exception { + mMockingSession = mockitoSession() + .initMocks(this) + .strictness(Strictness.LENIENT) + .mockStatic(UserHandle.class) + .startMocking(); + doReturn(USER_ID).when(() -> UserHandle.getUserId(UID_1)); + + ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.uid = UID_1; + when(mPackageManager.getApplicationInfoAsUser(PACKAGE_NAME_1, 0, USER_ID)) + .thenReturn(applicationInfo); + + mContext = new ContextWrapper(mTestableContext) { + @Override + public PackageManager getPackageManager() { + return mPackageManager; + } + }; + } + + @After + public void tearDown() { + if (mMockingSession != null) { + mMockingSession.finishMocking(); + } + } + @Test public void testSingleRateLimit_belowLimit_isWithinQuota() { MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector) @@ -194,4 +242,48 @@ public class MultiRateLimiterTest { assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue(); } + + @Test + public void clearRateLimiterForPackage_afterReachingQuota_quotaIsReset() { + MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector) + .addRateLimit(1, Duration.ofSeconds(100)) + .build(); + + mInjector.mElapsedTime = Duration.ZERO; + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofSeconds(1); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse(); + + multiRateLimiter.clear(USER_ID, PACKAGE_NAME_1); + + // Quota for that package is reset. + mInjector.mElapsedTime = Duration.ofSeconds(1); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + + // Quota is enforced again. + mInjector.mElapsedTime = Duration.ofSeconds(1); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse(); + } + + @Test + public void clearRateLimiterForPackage_doesntAffectOtherPackages() { + MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector) + .addRateLimit(1, Duration.ofSeconds(100)) + .build(); + + mInjector.mElapsedTime = Duration.ZERO; + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_2, TAG); + + mInjector.mElapsedTime = Duration.ofSeconds(1); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isFalse(); + + multiRateLimiter.clear(USER_ID, PACKAGE_NAME_1); + + // Doesn't affect the other package. + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isFalse(); + } } 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/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index a455ba90ccfe..065a2f307f5b 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -107,6 +107,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.internal.messages.nano.SystemMessageProto; +import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockscreenCredential; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -2333,6 +2334,32 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + public void testSetPermittedInputMethodsWithPOOfOrganizationOwnedDevice() + throws Exception { + String packageName = "com.google.pkg.one"; + setupProfileOwner(); + configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE); + + // Allow all input methods + parentDpm.setPermittedInputMethods(admin1, null); + + assertThat(parentDpm.getPermittedInputMethods(admin1)).isNull(); + + // Allow only system input methods + parentDpm.setPermittedInputMethods(admin1, new ArrayList<>()); + + assertThat(parentDpm.getPermittedInputMethods(admin1)).isEmpty(); + + // Don't allow specific third party input methods + final List<String> inputMethods = Collections.singletonList(packageName); + + assertExpectException(IllegalArgumentException.class, /* messageRegex= */ "Permitted " + + "input methods must allow all input methods or only system input methods " + + "when called on the parent instance of an organization-owned device", + () -> parentDpm.setPermittedInputMethods(admin1, inputMethods)); + } + + @Test public void testSetKeyguardDisabledFeaturesWithDO() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -5133,6 +5160,54 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + public void testGetAggregatedPasswordComplexity_IgnoreProfileRequirement() + throws Exception { + final int managedProfileUserId = CALLER_USER_HANDLE; + final int managedProfileAdminUid = + UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); + mContext.binder.callingUid = managedProfileAdminUid; + addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R); + + dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH); + parentDpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_LOW); + + assertThat(dpms.getAggregatedPasswordComplexityForUser(UserHandle.USER_SYSTEM, true)) + .isEqualTo(PASSWORD_COMPLEXITY_LOW); + assertThat(dpms.getAggregatedPasswordComplexityForUser(UserHandle.USER_SYSTEM, false)) + .isEqualTo(PASSWORD_COMPLEXITY_HIGH); + } + + @Test + public void testGetAggregatedPasswordMetrics_IgnoreProfileRequirement() + throws Exception { + final int managedProfileUserId = CALLER_USER_HANDLE; + final int managedProfileAdminUid = + UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); + mContext.binder.callingUid = managedProfileAdminUid; + addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R); + + dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); + dpm.setPasswordMinimumLength(admin1, 8); + dpm.setPasswordMinimumLetters(admin1, 1); + dpm.setPasswordMinimumNumeric(admin1, 2); + dpm.setPasswordMinimumSymbols(admin1, 3); + + parentDpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); + + PasswordMetrics deviceMetrics = + dpms.getPasswordMinimumMetrics(UserHandle.USER_SYSTEM, true); + assertThat(deviceMetrics.credType).isEqualTo(LockPatternUtils.CREDENTIAL_TYPE_PATTERN); + + PasswordMetrics allMetrics = + dpms.getPasswordMinimumMetrics(UserHandle.USER_SYSTEM, false); + assertThat(allMetrics.credType).isEqualTo(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD); + assertThat(allMetrics.length).isEqualTo(8); + assertThat(allMetrics.letters).isEqualTo(1); + assertThat(allMetrics.numeric).isEqualTo(2); + assertThat(allMetrics.symbols).isEqualTo(3); + } + + @Test public void testCanSetPasswordRequirementOnParentPreS() throws Exception { final int managedProfileUserId = CALLER_USER_HANDLE; final int managedProfileAdminUid = 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/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java index 6e4d994bd416..44418ce1e9c4 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java @@ -105,6 +105,10 @@ public class ArcInitiationActionFromAvrTest { } @Override + protected void writeStringSystemProperty(String key, String value) { + } + + @Override Looper getServiceLooper() { return mTestLooper.getLooper(); } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java index bbe1156c5d61..d454d8771e15 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java @@ -106,6 +106,10 @@ public class ArcTerminationActionFromAvrTest { } @Override + protected void writeStringSystemProperty(String key, String value) { + } + + @Override Looper getServiceLooper() { return mTestLooper.getLooper(); } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java index aeeca1a39cc0..8e37f94828fb 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java @@ -95,6 +95,15 @@ final class FakeHdmiCecConfig extends HdmiCecConfig { + " </allowed-values>" + " <default-value int-value=\"1\" />" + " </setting>" + + " <setting name=\"volume_control_enabled\"" + + " value-type=\"int\"" + + " user-configurable=\"true\">" + + " <allowed-values>" + + " <value int-value=\"0\" />" + + " <value int-value=\"1\" />" + + " </allowed-values>" + + " <default-value int-value=\"1\" />" + + " </setting>" + "</cec-settings>"; FakeHdmiCecConfig(@NonNull Context context) { diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java index 6bb68da2a894..95a0a7439904 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java @@ -195,8 +195,9 @@ public class HdmiCecLocalDeviceAudioSystemTest { } }; - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); - + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, + HdmiControlManager.VOLUME_CONTROL_ENABLED); mMyLooper = mTestLooper.getLooper(); mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService); mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService) { @@ -710,7 +711,8 @@ public class HdmiCecLocalDeviceAudioSystemTest { public void giveAudioStatus_volumeEnabled() { mMusicVolume = 50; mMusicMaxVolume = 100; - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_ENABLED); mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true); int volume = mHdmiControlService.getAudioManager() @@ -740,7 +742,8 @@ public class HdmiCecLocalDeviceAudioSystemTest { public void giveAudioStatus_volumeDisabled() { mMusicVolume = 50; mMusicMaxVolume = 100; - mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_DISABLED); mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true); int volume = mHdmiControlService.getAudioManager() @@ -770,7 +773,8 @@ public class HdmiCecLocalDeviceAudioSystemTest { public void reportAudioStatus_volumeEnabled() { mMusicVolume = 50; mMusicMaxVolume = 100; - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_ENABLED); mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true); int volume = mHdmiControlService.getAudioManager() @@ -794,7 +798,8 @@ public class HdmiCecLocalDeviceAudioSystemTest { public void reportAudioStatus_volumeDisabled() { mMusicVolume = 50; mMusicMaxVolume = 100; - mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_DISABLED); mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true); int volume = mHdmiControlService.getAudioManager() diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index 7aea4ffe1b03..bc808762b855 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -862,7 +862,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void sendVolumeKeyEvent_up_volumeEnabled() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_ENABLED); mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true); mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false); mTestLooper.dispatchAll(); @@ -879,7 +880,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void sendVolumeKeyEvent_down_volumeEnabled() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_ENABLED); mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, true); mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, false); mTestLooper.dispatchAll(); @@ -896,7 +898,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void sendVolumeKeyEvent_mute_volumeEnabled() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_ENABLED); mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, true); mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, false); mTestLooper.dispatchAll(); @@ -913,7 +916,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void sendVolumeKeyEvent_up_volumeDisabled() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_DISABLED); mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true); mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false); mTestLooper.dispatchAll(); @@ -930,7 +934,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void sendVolumeKeyEvent_down_volumeDisabled() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_DISABLED); mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, true); mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, false); mTestLooper.dispatchAll(); @@ -947,7 +952,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void sendVolumeKeyEvent_mute_volumeDisabled() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_DISABLED); mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, true); mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, false); mTestLooper.dispatchAll(); @@ -964,7 +970,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void sendVolumeKeyEvent_toTv_activeSource() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_ENABLED); mHdmiControlService.setSystemAudioActivated(false); mHdmiControlService.setActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); @@ -984,7 +991,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void sendVolumeKeyEvent_toAudio_activeSource() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_ENABLED); mHdmiControlService.setSystemAudioActivated(true); mHdmiControlService.setActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); @@ -1004,7 +1012,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void sendVolumeKeyEvent_toTv_inactiveSource() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_ENABLED); mHdmiControlService.setSystemAudioActivated(false); mHdmiControlService.setActiveSource(ADDR_TV, 0x0000, "HdmiCecLocalDevicePlaybackTest"); @@ -1023,7 +1032,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void sendVolumeKeyEvent_toAudio_inactiveSource() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_ENABLED); mHdmiControlService.setSystemAudioActivated(true); mHdmiControlService.setActiveSource(ADDR_TV, 0x0000, "HdmiCecLocalDevicePlaybackTest"); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java index eb2f9608f6f3..0717112da12c 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java @@ -32,6 +32,7 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import android.content.Context; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPortInfo; import android.os.test.TestLooper; @@ -124,8 +125,13 @@ public class HdmiCecLocalDeviceTest { @Before public void SetUp() { + + Context context = InstrumentationRegistry.getTargetContext(); + + HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(context); + mHdmiControlService = - new HdmiControlService(InstrumentationRegistry.getTargetContext()) { + new HdmiControlService(context) { @Override boolean isControlEnabled() { return isControlEnabled; @@ -157,6 +163,11 @@ public class HdmiCecLocalDeviceTest { void wakeUp() { mWakeupMessageReceived = true; } + + @Override + protected HdmiCecConfig getHdmiCecConfig() { + return hdmiCecConfig; + } }; mHdmiControlService.setIoLooper(mTestLooper.getLooper()); mNativeWrapper = new FakeNativeWrapper(); @@ -244,7 +255,8 @@ public class HdmiCecLocalDeviceTest { @Test public void handleUserControlPressed_volumeUp() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_ENABLED); boolean result = mHdmiLocalDevice.handleUserControlPressed( HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV, HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP)); @@ -254,7 +266,8 @@ public class HdmiCecLocalDeviceTest { @Test public void handleUserControlPressed_volumeDown() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_ENABLED); boolean result = mHdmiLocalDevice.handleUserControlPressed( HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV, HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN)); @@ -264,7 +277,8 @@ public class HdmiCecLocalDeviceTest { @Test public void handleUserControlPressed_volumeMute() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_ENABLED); boolean result = mHdmiLocalDevice.handleUserControlPressed( HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV, HdmiCecKeycode.CEC_KEYCODE_MUTE)); @@ -274,7 +288,8 @@ public class HdmiCecLocalDeviceTest { @Test public void handleUserControlPressed_volumeUp_disabled() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_DISABLED); boolean result = mHdmiLocalDevice.handleUserControlPressed( HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV, HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP)); @@ -284,7 +299,8 @@ public class HdmiCecLocalDeviceTest { @Test public void handleUserControlPressed_volumeDown_disabled() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_DISABLED); boolean result = mHdmiLocalDevice.handleUserControlPressed( HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV, HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN)); @@ -294,7 +310,8 @@ public class HdmiCecLocalDeviceTest { @Test public void handleUserControlPressed_volumeMute_disabled() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_DISABLED); boolean result = mHdmiLocalDevice.handleUserControlPressed( HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV, HdmiCecKeycode.CEC_KEYCODE_MUTE)); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java index c212bf868c76..70718f765412 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java @@ -136,6 +136,8 @@ public class HdmiControlServiceBinderAPITest { // Some tests expect no logical addresses being allocated at the beginning of the test. setHdmiControlEnabled(false); + HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContext); + mHdmiControlService = new HdmiControlService(mContext) { @Override @@ -158,6 +160,11 @@ public class HdmiControlServiceBinderAPITest { boolean isPowerStandby() { return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY; } + + @Override + protected HdmiCecConfig getHdmiCecConfig() { + return hdmiCecConfig; + } }; mMyLooper = mTestLooper.getLooper(); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 9152e1e84b71..be584d7b4591 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -154,6 +154,10 @@ public class HdmiControlServiceTest { } @Override + protected void writeStringSystemProperty(String key, String value) { + } + + @Override protected HdmiCecConfig getHdmiCecConfig() { return hdmiCecConfig; } @@ -251,7 +255,7 @@ public class HdmiControlServiceTest { HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, HdmiControlManager.HDMI_CEC_VERSION_1_4_B); - mHdmiControlService.setControlEnabled(true); + mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); mNativeWrapper.clearResultMessages(); assertThat(mHdmiControlService.getInitialPowerStatus()).isEqualTo( @@ -271,7 +275,7 @@ public class HdmiControlServiceTest { HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, HdmiControlManager.HDMI_CEC_VERSION_2_0); - mHdmiControlService.setControlEnabled(true); + mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); mNativeWrapper.clearResultMessages(); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); @@ -291,136 +295,175 @@ public class HdmiControlServiceTest { @Test public void setAndGetCecVolumeControlEnabled_isApi() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(false); - assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isFalse(); + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, + HdmiControlManager.VOLUME_CONTROL_DISABLED); + assertThat(mHdmiControlService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE)).isEqualTo( + HdmiControlManager.VOLUME_CONTROL_DISABLED); - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); - assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isTrue(); + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, + HdmiControlManager.VOLUME_CONTROL_ENABLED); + assertThat(mHdmiControlService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE)).isEqualTo( + HdmiControlManager.VOLUME_CONTROL_ENABLED); } @Test public void setAndGetCecVolumeControlEnabled_changesSetting() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(false); - assertThat(mHdmiControlService.readBooleanSetting( - Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true)).isFalse(); + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, + HdmiControlManager.VOLUME_CONTROL_DISABLED); + assertThat(mHdmiControlService.readIntSetting( + Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, -1)).isEqualTo( + HdmiControlManager.VOLUME_CONTROL_DISABLED); - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); - assertThat(mHdmiControlService.readBooleanSetting( - Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true)).isTrue(); + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, + HdmiControlManager.VOLUME_CONTROL_ENABLED); + assertThat(mHdmiControlService.readIntSetting( + Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, -1)).isEqualTo( + HdmiControlManager.VOLUME_CONTROL_ENABLED); } @Test public void setAndGetCecVolumeControlEnabledInternal_doesNotChangeSetting() { - mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(true); - - mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(false); - assertThat(mHdmiControlService.readBooleanSetting( - Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true)).isTrue(); - - mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(true); - assertThat(mHdmiControlService.readBooleanSetting( - Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true)).isTrue(); + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, + HdmiControlManager.VOLUME_CONTROL_ENABLED); + + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_DISABLED); + assertThat(mHdmiControlService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE)).isEqualTo( + HdmiControlManager.VOLUME_CONTROL_ENABLED); + + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_ENABLED); + assertThat(mHdmiControlService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE)).isEqualTo( + HdmiControlManager.VOLUME_CONTROL_ENABLED); } @Test public void disableAndReenableCec_volumeControlReturnsToOriginalValue_enabled() { - boolean volumeControlEnabled = true; - mHdmiControlService.setHdmiCecVolumeControlEnabled(volumeControlEnabled); + int volumeControlEnabled = HdmiControlManager.VOLUME_CONTROL_ENABLED; + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(volumeControlEnabled); - mHdmiControlService.setControlEnabled(false); - assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isFalse(); + mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + assertThat(mHdmiControlService.getHdmiCecVolumeControl()).isEqualTo( + HdmiControlManager.VOLUME_CONTROL_DISABLED); - mHdmiControlService.setControlEnabled(true); - assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isEqualTo( - volumeControlEnabled); + mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + assertThat(mHdmiControlService.getHdmiCecVolumeControl()).isEqualTo(volumeControlEnabled); } @Test public void disableAndReenableCec_volumeControlReturnsToOriginalValue_disabled() { - boolean volumeControlEnabled = false; - mHdmiControlService.setHdmiCecVolumeControlEnabled(volumeControlEnabled); + int volumeControlEnabled = HdmiControlManager.VOLUME_CONTROL_DISABLED; + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, volumeControlEnabled); - mHdmiControlService.setControlEnabled(false); - assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isFalse(); + mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + assertThat(mHdmiControlService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE)).isEqualTo( + volumeControlEnabled); - mHdmiControlService.setControlEnabled(true); - assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isEqualTo( + mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + assertThat(mHdmiControlService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE)).isEqualTo( volumeControlEnabled); } @Test public void disableAndReenableCec_volumeControlFeatureListenersNotified() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, + HdmiControlManager.VOLUME_CONTROL_ENABLED); VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback(); mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback); - mHdmiControlService.setControlEnabled(false); + mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); assertThat(callback.mCallbackReceived).isTrue(); - assertThat(callback.mVolumeControlEnabled).isFalse(); + assertThat(callback.mVolumeControlEnabled).isEqualTo( + HdmiControlManager.VOLUME_CONTROL_DISABLED); - mHdmiControlService.setControlEnabled(true); - assertThat(callback.mVolumeControlEnabled).isTrue(); + mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + assertThat(callback.mVolumeControlEnabled).isEqualTo( + HdmiControlManager.VOLUME_CONTROL_ENABLED); } @Test public void addHdmiCecVolumeControlFeatureListener_emitsCurrentState_enabled() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_ENABLED); VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback(); mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback); mTestLooper.dispatchAll(); assertThat(callback.mCallbackReceived).isTrue(); - assertThat(callback.mVolumeControlEnabled).isTrue(); + assertThat(callback.mVolumeControlEnabled).isEqualTo( + HdmiControlManager.VOLUME_CONTROL_ENABLED); } @Test public void addHdmiCecVolumeControlFeatureListener_emitsCurrentState_disabled() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_DISABLED); VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback(); mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback); mTestLooper.dispatchAll(); assertThat(callback.mCallbackReceived).isTrue(); - assertThat(callback.mVolumeControlEnabled).isFalse(); + assertThat(callback.mVolumeControlEnabled).isEqualTo( + HdmiControlManager.VOLUME_CONTROL_DISABLED); } @Test public void addHdmiCecVolumeControlFeatureListener_notifiesStateUpdate() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_DISABLED); VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback(); mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback); - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_ENABLED); mTestLooper.dispatchAll(); assertThat(callback.mCallbackReceived).isTrue(); - assertThat(callback.mVolumeControlEnabled).isTrue(); + assertThat(callback.mVolumeControlEnabled).isEqualTo( + HdmiControlManager.VOLUME_CONTROL_ENABLED); } @Test public void addHdmiCecVolumeControlFeatureListener_honorsUnregistration() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_DISABLED); VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback(); mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback); mTestLooper.dispatchAll(); mHdmiControlService.removeHdmiControlVolumeControlStatusChangeListener(callback); - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_ENABLED); mTestLooper.dispatchAll(); assertThat(callback.mCallbackReceived).isTrue(); - assertThat(callback.mVolumeControlEnabled).isFalse(); + assertThat(callback.mVolumeControlEnabled).isEqualTo( + HdmiControlManager.VOLUME_CONTROL_DISABLED); } @Test public void addHdmiCecVolumeControlFeatureListener_notifiesStateUpdate_multiple() { - mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_DISABLED); VolumeControlFeatureCallback callback1 = new VolumeControlFeatureCallback(); VolumeControlFeatureCallback callback2 = new VolumeControlFeatureCallback(); @@ -428,13 +471,16 @@ public class HdmiControlServiceTest { mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback2); - mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setHdmiCecVolumeControlEnabledInternal( + HdmiControlManager.VOLUME_CONTROL_ENABLED); mTestLooper.dispatchAll(); assertThat(callback1.mCallbackReceived).isTrue(); assertThat(callback2.mCallbackReceived).isTrue(); - assertThat(callback1.mVolumeControlEnabled).isTrue(); - assertThat(callback2.mVolumeControlEnabled).isTrue(); + assertThat(callback1.mVolumeControlEnabled).isEqualTo( + HdmiControlManager.VOLUME_CONTROL_ENABLED); + assertThat(callback2.mVolumeControlEnabled).isEqualTo( + HdmiControlManager.VOLUME_CONTROL_ENABLED); } @Test @@ -443,7 +489,7 @@ public class HdmiControlServiceTest { Settings.Global.putString(mContextSpy.getContentResolver(), Settings.Global.HDMI_CEC_VERSION, null); - mHdmiControlService.setControlEnabled(true); + mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); assertThat(mHdmiControlService.getCecVersion()).isEqualTo( HdmiControlManager.HDMI_CEC_VERSION_1_4_B); } @@ -453,7 +499,7 @@ public class HdmiControlServiceTest { mHdmiControlService.getHdmiCecConfig().setIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, HdmiControlManager.HDMI_CEC_VERSION_1_4_B); - mHdmiControlService.setControlEnabled(true); + mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); assertThat(mHdmiControlService.getCecVersion()).isEqualTo( HdmiControlManager.HDMI_CEC_VERSION_1_4_B); } @@ -463,7 +509,7 @@ public class HdmiControlServiceTest { mHdmiControlService.getHdmiCecConfig().setIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, HdmiControlManager.HDMI_CEC_VERSION_2_0); - mHdmiControlService.setControlEnabled(true); + mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); assertThat(mHdmiControlService.getCecVersion()).isEqualTo( HdmiControlManager.HDMI_CEC_VERSION_2_0); } @@ -473,14 +519,14 @@ public class HdmiControlServiceTest { mHdmiControlService.getHdmiCecConfig().setIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, HdmiControlManager.HDMI_CEC_VERSION_1_4_B); - mHdmiControlService.setControlEnabled(true); + mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); assertThat(mHdmiControlService.getCecVersion()).isEqualTo( HdmiControlManager.HDMI_CEC_VERSION_1_4_B); mHdmiControlService.getHdmiCecConfig().setIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, HdmiControlManager.HDMI_CEC_VERSION_2_0); - mHdmiControlService.setControlEnabled(true); + mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); assertThat(mHdmiControlService.getCecVersion()).isEqualTo( HdmiControlManager.HDMI_CEC_VERSION_2_0); } @@ -490,7 +536,7 @@ public class HdmiControlServiceTest { mHdmiControlService.getHdmiCecConfig().setIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, HdmiControlManager.HDMI_CEC_VERSION_1_4_B); - mHdmiControlService.setControlEnabled(true); + mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); mTestLooper.dispatchAll(); mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildGiveFeatures(Constants.ADDR_TV, @@ -508,7 +554,7 @@ public class HdmiControlServiceTest { mHdmiControlService.getHdmiCecConfig().setIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, HdmiControlManager.HDMI_CEC_VERSION_2_0); - mHdmiControlService.setControlEnabled(true); + mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mTestLooper.dispatchAll(); @@ -530,7 +576,7 @@ public class HdmiControlServiceTest { mHdmiControlService.getHdmiCecConfig().setIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, HdmiControlManager.HDMI_CEC_VERSION_1_4_B); - mHdmiControlService.setControlEnabled(true); + mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mTestLooper.dispatchAll(); @@ -547,7 +593,7 @@ public class HdmiControlServiceTest { mHdmiControlService.getHdmiCecConfig().setIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, HdmiControlManager.HDMI_CEC_VERSION_2_0); - mHdmiControlService.setControlEnabled(true); + mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mTestLooper.dispatchAll(); @@ -579,10 +625,10 @@ public class HdmiControlServiceTest { private static class VolumeControlFeatureCallback extends IHdmiCecVolumeControlFeatureListener.Stub { boolean mCallbackReceived = false; - boolean mVolumeControlEnabled = false; + int mVolumeControlEnabled = -1; @Override - public void onHdmiCecVolumeControlFeature(boolean enabled) throws RemoteException { + public void onHdmiCecVolumeControlFeature(int enabled) throws RemoteException { this.mCallbackReceived = true; this.mVolumeControlEnabled = enabled; } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java index f80b5737d27b..f9160abcbfbf 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java @@ -138,6 +138,10 @@ public class SystemAudioInitiationActionFromAvrTest { } @Override + protected void writeStringSystemProperty(String key, String value) { + } + + @Override void wakeUp() {} @Override 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/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java index be8a99c5ce36..63330d518297 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java @@ -42,12 +42,14 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.AlarmManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -141,6 +143,7 @@ public final class DataManagerTest { @Mock private JobScheduler mJobScheduler; @Mock private StatusBarNotification mStatusBarNotification; @Mock private Notification mNotification; + @Mock private AlarmManager mAlarmManager; @Captor private ArgumentCaptor<ShortcutChangeCallback> mShortcutChangeCallbackCaptor; @Captor private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor; @@ -152,7 +155,6 @@ public final class DataManagerTest { private DataManager mDataManager; private CancellationSignal mCancellationSignal; private ShortcutChangeCallback mShortcutChangeCallback; - private BroadcastReceiver mShutdownBroadcastReceiver; private ShortcutInfo mShortcutInfo; private TestInjector mInjector; @@ -187,10 +189,15 @@ public final class DataManagerTest { Context originalContext = getInstrumentation().getTargetContext(); when(mContext.getApplicationInfo()).thenReturn(originalContext.getApplicationInfo()); + when(mContext.getUser()).thenReturn(originalContext.getUser()); + when(mContext.getPackageName()).thenReturn(originalContext.getPackageName()); when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); when(mContext.getSystemServiceName(UserManager.class)).thenReturn( Context.USER_SERVICE); + when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager); + when(mContext.getSystemServiceName(AlarmManager.class)).thenReturn( + Context.ALARM_SERVICE); when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager); @@ -246,8 +253,7 @@ public final class DataManagerTest { mShortcutChangeCallbackCaptor.capture()); mShortcutChangeCallback = mShortcutChangeCallbackCaptor.getValue(); - verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), any()); - mShutdownBroadcastReceiver = mBroadcastReceiverCaptor.getValue(); + verify(mContext, times(2)).registerReceiver(any(), any()); } @After @@ -767,6 +773,36 @@ public final class DataManagerTest { } @Test + public void testPruneExpiredConversationStatuses() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + + ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, + buildPerson()); + mDataManager.addOrUpdateConversationInfo(shortcut); + + ConversationStatus cs1 = new ConversationStatus.Builder("cs1", 9) + .setEndTimeMillis(System.currentTimeMillis()) + .build(); + ConversationStatus cs2 = new ConversationStatus.Builder("cs2", 10) + .build(); + ConversationStatus cs3 = new ConversationStatus.Builder("cs3", 1) + .setEndTimeMillis(Long.MAX_VALUE) + .build(); + mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs1); + mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2); + mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs3); + + mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal); + + assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID)) + .doesNotContain(cs1); + assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID)) + .contains(cs2); + assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID)) + .contains(cs3); + } + + @Test public void testDoNotUncacheShortcutWithActiveNotifications() { mDataManager.onUserUnlocked(USER_ID_PRIMARY); NotificationListenerService listenerService = @@ -976,6 +1012,29 @@ public final class DataManagerTest { .contains(cs); assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID)) .contains(cs2); + + verify(mAlarmManager, never()).setExactAndAllowWhileIdle(anyInt(), anyLong(), any()); + } + + @Test + public void testAddOrUpdateStatus_schedulesJob() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + + ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, + buildPerson()); + mDataManager.addOrUpdateConversationInfo(shortcut); + + ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY) + .setEndTimeMillis(1000) + .build(); + mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs); + + ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME) + .setEndTimeMillis(3000) + .build(); + mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2); + + verify(mAlarmManager, times(2)).setExactAndAllowWhileIdle(anyInt(), anyLong(), any()); } @Test 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 ecdb8bc71bd8..d8679876965c 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import android.app.ActivityManager; import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; @@ -36,7 +37,6 @@ import android.media.AudioManager; import android.os.Handler; import android.os.PowerManagerInternal; import android.os.PowerSaveState; -import android.os.RemoteException; import android.os.UserHandle; import android.os.VibrationAttributes; import android.os.VibrationEffect; @@ -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; @@ -229,9 +228,23 @@ public class VibrationSettingsTest { } @Test - public void shouldVibrateForUid_withBackgroundAllowedUsage_returnTrue() throws RemoteException { + public void shouldVibrateForUid_withForegroundOnlyUsage_returnsTrueWhInForeground() { + assertTrue(mVibrationSettings.shouldVibrateForUid(UID, VibrationAttributes.USAGE_TOUCH)); + + mVibrationSettings.mUidObserver.onUidStateChanged( + UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0); + assertFalse(mVibrationSettings.shouldVibrateForUid(UID, VibrationAttributes.USAGE_TOUCH)); + } + + @Test + public void shouldVibrateForUid_withBackgroundAllowedUsage_returnTrue() { + mVibrationSettings.mUidObserver.onUidStateChanged( + UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0); + assertTrue(mVibrationSettings.shouldVibrateForUid(UID, VibrationAttributes.USAGE_ALARM)); assertTrue(mVibrationSettings.shouldVibrateForUid(UID, + VibrationAttributes.USAGE_COMMUNICATION_REQUEST)); + assertTrue(mVibrationSettings.shouldVibrateForUid(UID, VibrationAttributes.USAGE_NOTIFICATION)); assertTrue(mVibrationSettings.shouldVibrateForUid(UID, VibrationAttributes.USAGE_RINGTONE)); } @@ -291,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); @@ -322,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); @@ -377,5 +384,4 @@ public class VibrationSettingsTest { mAudioManager.setRingerModeInternal(ringerMode); assertEquals(ringerMode, mAudioManager.getRingerModeInternal()); } - } 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..ef96a2d5960a 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; @@ -1063,6 +1064,21 @@ public class ActivityRecordTests extends WindowTestsBase { } /** + * Verify that finish bottom activity from a task won't boost it to top. + */ + @Test + public void testFinishBottomActivityIfPossible_noZBoost() { + final ActivityRecord bottomActivity = createActivityWithTask(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm) + .setTask(bottomActivity.getTask()).build(); + topActivity.mVisibleRequested = true; + // simulating bottomActivity as a trampoline activity. + bottomActivity.setState(RESUMED, "test"); + bottomActivity.finishIfPossible("test", false); + assertFalse(bottomActivity.mNeedsZBoost); + } + + /** * Verify that complete finish request for visible activity must be delayed before the next one * becomes visible. */ @@ -1449,6 +1465,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/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java index 6f5a874114ea..5c67db7ef813 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java @@ -514,7 +514,7 @@ public class DisplayAreaTest extends WindowTestsBase { return new WindowState(mWm, mock(Session.class), new TestIWindow(), token, null /* parentWindow */, 0 /* appOp */, new WindowManager.LayoutParams(), View.VISIBLE, 0 /* ownerId */, 0 /* showUserId */, - false /* ownerCanAddInternalSystemWindow */); + false /* ownerCanAddInternalSystemWindow */, false /* ownerCanUseBackgroundBlur */); } private WindowToken createWindowToken(int type) { 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/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 8b93372e5a76..c85991d8f3b2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -340,7 +340,7 @@ class WindowTestsBase extends SystemServiceTestsBase { final WindowState w = new WindowState(service, session, iWindow, token, parent, OP_NONE, attrs, VISIBLE, ownerId, userId, - ownerCanAddInternalSystemWindow, + ownerCanAddInternalSystemWindow, false /* ownerCanUseBackgroundBlur */, powerManagerWrapper); // TODO: Probably better to make this call in the WindowState ctor to avoid errors with // adding it to the token... @@ -1213,7 +1213,8 @@ class WindowTestsBase extends SystemServiceTestsBase { TestWindowState(WindowManagerService service, Session session, IWindow window, WindowManager.LayoutParams attrs, WindowToken token) { super(service, session, window, token, null, OP_NONE, attrs, 0, 0, 0, - false /* ownerCanAddInternalSystemWindow */); + false /* ownerCanAddInternalSystemWindow */, + false /* ownerCanUseBackgroundBlur */); } @Override 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/common/com/android/internal/telephony/SipMessageParsingUtils.java b/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java new file mode 100644 index 000000000000..c7e7cd5ec64e --- /dev/null +++ b/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import android.net.Uri; +import android.util.Log; +import android.util.Pair; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Utility methods for parsing parts of {@link android.telephony.ims.SipMessage}s. + * See RFC 3261 for more information. + * @hide + */ +// Note: This is lightweight in order to avoid a full SIP stack import in frameworks/base. +public class SipMessageParsingUtils { + private static final String TAG = "SipMessageParsingUtils"; + // "Method" in request-line + // Request-Line = Method SP Request-URI SP SIP-Version CRLF + private static final String[] SIP_REQUEST_METHODS = new String[] {"INVITE", "ACK", "OPTIONS", + "BYE", "CANCEL", "REGISTER", "PRACK", "SUBSCRIBE", "NOTIFY", "PUBLISH", "INFO", "REFER", + "MESSAGE", "UPDATE"}; + + // SIP Version 2.0 (corresponding to RCS 3261), set in "SIP-Version" of Status-Line and + // Request-Line + // + // Request-Line = Method SP Request-URI SP SIP-Version CRLF + // Status-Line = SIP-Version SP Status-Code SP Reason-Phrase CRLF + private static final String SIP_VERSION_2 = "SIP/2.0"; + + // headers are formatted Key:Value + private static final String HEADER_KEY_VALUE_SEPARATOR = ":"; + // Multiple of the same header can be concatenated and put into one header Key:Value pair, for + // example "v: XX1;branch=YY1,XX2;branch=YY2". This needs to be treated as two "v:" headers. + private static final String SUBHEADER_VALUE_SEPARATOR = ","; + + // SIP header parameters have the format ";paramName=paramValue" + private static final String PARAM_SEPARATOR = ";"; + // parameters are formatted paramName=ParamValue + private static final String PARAM_KEY_VALUE_SEPARATOR = "="; + + // The via branch parameter definition + private static final String BRANCH_PARAM_KEY = "branch"; + + // via header key + private static final String VIA_SIP_HEADER_KEY = "via"; + // compact form of the via header key + private static final String VIA_SIP_HEADER_KEY_COMPACT = "v"; + + /** + * @return true if the SIP message start line is considered a request (based on known request + * methods). + */ + public static boolean isSipRequest(String startLine) { + String[] splitLine = splitStartLineAndVerify(startLine); + if (splitLine == null) return false; + return verifySipRequest(splitLine); + } + + /** + * Return the via branch parameter, which is used to identify the transaction ID (request and + * response pair) in a SIP transaction. + * @param headerString The string containing the headers of the SIP message. + */ + public static String getTransactionId(String headerString) { + // search for Via: or v: parameter, we only care about the first one. + List<Pair<String, String>> headers = parseHeaders(headerString, true, + VIA_SIP_HEADER_KEY, VIA_SIP_HEADER_KEY_COMPACT); + for (Pair<String, String> header : headers) { + // Headers can also be concatenated together using a "," between each header value. + // format becomes v: XX1;branch=YY1,XX2;branch=YY2. Need to extract only the first ID's + // branch param YY1. + String[] subHeaders = header.second.split(SUBHEADER_VALUE_SEPARATOR); + for (String subHeader : subHeaders) { + // Search for ;branch=z9hG4bKXXXXXX and return parameter value + String[] params = subHeader.split(PARAM_SEPARATOR); + if (params.length < 2) { + // This param doesn't include a branch param, move to next param. + Log.w(TAG, "getTransactionId: via detected without branch param:" + + subHeader); + continue; + } + // by spec, each param can only appear once in a header. + for (String param : params) { + String[] pair = param.split(PARAM_KEY_VALUE_SEPARATOR); + if (pair.length < 2) { + // ignore info before the first parameter + continue; + } + if (pair.length > 2) { + Log.w(TAG, + "getTransactionId: unexpected parameter" + Arrays.toString(pair)); + } + // Trim whitespace in parameter + pair[0] = pair[0].trim(); + pair[1] = pair[1].trim(); + if (BRANCH_PARAM_KEY.equalsIgnoreCase(pair[0])) { + // There can be multiple "Via" headers in the SIP message, however we want + // to return the first once found, as this corresponds with the transaction + // that is relevant here. + return pair[1]; + } + } + } + } + return null; + } + + private static String[] splitStartLineAndVerify(String startLine) { + String[] splitLine = startLine.split(" "); + if (isStartLineMalformed(splitLine)) return null; + return splitLine; + } + + private static boolean isStartLineMalformed(String[] startLine) { + if (startLine == null || startLine.length == 0) { + return true; + } + // start lines contain three segments separated by spaces (SP): + // Request-Line = Method SP Request-URI SP SIP-Version CRLF + // Status-Line = SIP-Version SP Status-Code SP Reason-Phrase CRLF + return (startLine.length != 3); + } + + private static boolean verifySipRequest(String[] request) { + // Request-Line = Method SP Request-URI SP SIP-Version CRLF + boolean verified = request[2].contains(SIP_VERSION_2); + verified &= (Uri.parse(request[1]).getScheme() != null); + verified &= Arrays.stream(SIP_REQUEST_METHODS).anyMatch(s -> request[0].contains(s)); + return verified; + } + + private static boolean verifySipResponse(String[] response) { + // Status-Line = SIP-Version SP Status-Code SP Reason-Phrase CRLF + boolean verified = response[0].contains(SIP_VERSION_2); + int statusCode = Integer.parseInt(response[1]); + verified &= (statusCode >= 100 && statusCode < 700); + return verified; + } + + /** + * Parse a String representation of the Header portion of the SIP Message and re-structure it + * into a List of key->value pairs representing each header in the order that they appeared in + * the message. + * + * @param headerString The raw string containing all headers + * @param stopAtFirstMatch Return early when the first match is found from matching header keys. + * @param matchingHeaderKeys An optional list of Strings containing header keys that should be + * returned if they exist. If none exist, all keys will be returned. + * (This is internally an equalsIgnoreMatch comparison). + * @return the matched header keys and values. + */ + private static List<Pair<String, String>> parseHeaders(String headerString, + boolean stopAtFirstMatch, String... matchingHeaderKeys) { + // Ensure there is no leading whitespace + headerString = removeLeadingWhitespace(headerString); + + List<Pair<String, String>> result = new ArrayList<>(); + // Split the string line-by-line. + String[] headerLines = headerString.split("\\r?\\n"); + if (headerLines.length == 0) { + return Collections.emptyList(); + } + + String headerKey = null; + StringBuilder headerValueSegment = new StringBuilder(); + // loop through each line, either parsing a "key: value" pair or appending values that span + // multiple lines. + for (String line : headerLines) { + // This line is a continuation of the last line if it starts with whitespace or tab + if (line.startsWith("\t") || line.startsWith(" ")) { + headerValueSegment.append(removeLeadingWhitespace(line)); + continue; + } + // This line is the start of a new key, If headerKey/value is already populated from a + // previous key/value pair, add it to list of parsed header pairs. + if (headerKey != null) { + final String key = headerKey; + if (matchingHeaderKeys == null || matchingHeaderKeys.length == 0 + || Arrays.stream(matchingHeaderKeys).anyMatch( + (s) -> s.equalsIgnoreCase(key))) { + result.add(new Pair<>(key, headerValueSegment.toString())); + if (stopAtFirstMatch) { + return result; + } + } + headerKey = null; + headerValueSegment = new StringBuilder(); + } + + // Format is "Key:Value", ignore any ":" after the first. + String[] pair = line.split(HEADER_KEY_VALUE_SEPARATOR, 2); + if (pair.length < 2) { + // malformed line, skip + Log.w(TAG, "parseHeaders - received malformed line: " + line); + continue; + } + + headerKey = pair[0].trim(); + for (int i = 1; i < pair.length; i++) { + headerValueSegment.append(removeLeadingWhitespace(pair[i])); + } + } + // Pick up the last pending header being parsed, if it exists. + if (headerKey != null) { + final String key = headerKey; + if (matchingHeaderKeys == null || matchingHeaderKeys.length == 0 + || Arrays.stream(matchingHeaderKeys).anyMatch( + (s) -> s.equalsIgnoreCase(key))) { + result.add(new Pair<>(key, headerValueSegment.toString())); + } + } + + return result; + } + + private static String removeLeadingWhitespace(String line) { + return line.replaceFirst("^\\s*", ""); + } +} 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 7e019ccde372..daa3d1f0c509 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -3591,6 +3591,28 @@ public class CarrierConfigManager { */ public static final String ENABLE_EAP_METHOD_PREFIX_BOOL = "enable_eap_method_prefix_bool"; + + /** + * Configs used by ImsServiceEntitlement. + */ + public static final class ImsServiceEntitlement { + private ImsServiceEntitlement() {} + + /** Prefix of all ImsServiceEntitlement.KEY_* constants. */ + public static final String KEY_PREFIX = "imsserviceentitlement."; + + + /** The address of the entitlement configuration server. */ + public static final String KEY_AES_URL_STRING = KEY_PREFIX + "aes_url_string"; + + + private static PersistableBundle getDefaults() { + PersistableBundle defaults = new PersistableBundle(); + defaults.putString(KEY_AES_URL_STRING, ""); + return defaults; + } + } + /** * GPS configs. See the GNSS HAL documentation for more details. */ @@ -4621,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; @@ -5119,6 +5148,7 @@ public class CarrierConfigManager { sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG, 10000); /* Default value is 60 seconds. */ sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG, 60000); + sDefaults.putAll(ImsServiceEntitlement.getDefaults()); sDefaults.putAll(Gps.getDefaults()); sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY, new int[] { @@ -5170,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); } /** @@ -5188,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/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index ed09d538a3b1..1273aa3abbc9 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -478,7 +478,9 @@ public class PhoneNumberUtils { /** * Compare phone numbers a and b, return true if they're identical enough for caller ID purposes. + * @deprecated use {@link #areSamePhoneNumber(String, String, String)} instead */ + @Deprecated public static boolean compare(String a, String b) { // We've used loose comparation at least Eclair, which may change in the future. @@ -489,7 +491,9 @@ public class PhoneNumberUtils { * Compare phone numbers a and b, and return true if they're identical * enough for caller ID purposes. Checks a resource to determine whether * to use a strict or loose comparison algorithm. + * @deprecated use {@link #areSamePhoneNumber(String, String, String)} instead */ + @Deprecated public static boolean compare(Context context, String a, String b) { boolean useStrict = context.getResources().getBoolean( com.android.internal.R.bool.config_use_strict_phone_number_comparation); @@ -3218,7 +3222,7 @@ public class PhoneNumberUtils { } // The conversion map is not defined (this is default). Skip conversion. - if (sConvertToEmergencyMap == null || sConvertToEmergencyMap.length == 0 ) { + if (sConvertToEmergencyMap == null || sConvertToEmergencyMap.length == 0) { return number; } @@ -3254,4 +3258,47 @@ public class PhoneNumberUtils { } return number; } + + /** + * Determines if two phone numbers are the same. + * <p> + * Matching is based on <a href="https://github.com/google/libphonenumber>libphonenumber</a>. + * Unlike {@link #compare(String, String)}, matching takes into account national + * dialing plans rather than simply matching the last 7 digits of the two phone numbers. As a + * result, it is expected that some numbers which would match using the previous method will no + * longer match using this new approach. + * + * @param number1 + * @param number2 + * @param defaultCountryIso The lowercase two letter ISO 3166-1 country code. Used when parsing + * the phone numbers where it is not possible to determine the country + * associated with a phone number based on the number alone. It + * is recommended to pass in + * {@link TelephonyManager#getNetworkCountryIso()}. + * @return True if the two given phone number are same. + */ + public static boolean areSamePhoneNumber(@NonNull String number1, + @NonNull String number2, @NonNull String defaultCountryIso) { + PhoneNumberUtil util = PhoneNumberUtil.getInstance(); + PhoneNumber n1; + PhoneNumber n2; + defaultCountryIso = defaultCountryIso.toUpperCase(); + try { + n1 = util.parseAndKeepRawInput(number1, defaultCountryIso); + n2 = util.parseAndKeepRawInput(number2, defaultCountryIso); + } catch (NumberParseException e) { + return false; + } + + PhoneNumberUtil.MatchType matchType = util.isNumberMatch(n1, n2); + if (matchType == PhoneNumberUtil.MatchType.EXACT_MATCH + || matchType == PhoneNumberUtil.MatchType.NSN_MATCH) { + return true; + } else if (matchType == PhoneNumberUtil.MatchType.SHORT_NSN_MATCH) { + return (n1.getNationalNumber() == n2.getNationalNumber() + && n1.getCountryCode() == n2.getCountryCode()); + } else { + return false; + } + } } 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 f6f6d75c37c6..0059ad6c2426 100644 --- a/telephony/java/android/telephony/SignalThresholdInfo.java +++ b/telephony/java/android/telephony/SignalThresholdInfo.java @@ -28,101 +28,109 @@ import java.util.Objects; /** * Defines the threshold value of the signal strength. - * @hide */ -public class SignalThresholdInfo implements Parcelable { +public final class SignalThresholdInfo implements Parcelable { + + /** + * Unknown signal measurement type. + */ + public static final int SIGNAL_MEASUREMENT_TYPE_UNKNOWN = 0; + /** * Received Signal Strength Indication. * Range: -113 dBm and -51 dBm - * Used RAN: GERAN, CDMA2000 + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#GERAN}, + * {@link AccessNetworkConstants.AccessNetworkType#CDMA2000} * Reference: 3GPP TS 27.007 section 8.5. */ - public static final int SIGNAL_RSSI = 1; + public static final int SIGNAL_MEASUREMENT_TYPE_RSSI = 1; /** * Received Signal Code Power. * Range: -120 dBm to -25 dBm; - * Used RAN: UTRAN + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#UTRAN} * Reference: 3GPP TS 25.123, section 9.1.1.1 */ - public static final int SIGNAL_RSCP = 2; + public static final int SIGNAL_MEASUREMENT_TYPE_RSCP = 2; /** * Reference Signal Received Power. * Range: -140 dBm to -44 dBm; - * Used RAN: EUTRAN + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN} * Reference: 3GPP TS 36.133 9.1.4 */ - public static final int SIGNAL_RSRP = 3; + public static final int SIGNAL_MEASUREMENT_TYPE_RSRP = 3; /** * Reference Signal Received Quality - * Range: -20 dB to -3 dB; - * Used RAN: EUTRAN + * Range: -34 dB to 3 dB; + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN} * Reference: 3GPP TS 36.133 9.1.7 */ - public static final int SIGNAL_RSRQ = 4; + public static final int SIGNAL_MEASUREMENT_TYPE_RSRQ = 4; /** * Reference Signal Signal to Noise Ratio * Range: -20 dB to 30 dB; - * Used RAN: EUTRAN + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN} */ - public static final int SIGNAL_RSSNR = 5; + public static final int SIGNAL_MEASUREMENT_TYPE_RSSNR = 5; /** * 5G SS reference signal received power. * Range: -140 dBm to -44 dBm. - * Used RAN: NGRAN + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN} * Reference: 3GPP TS 38.215. */ - public static final int SIGNAL_SSRSRP = 6; + public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRP = 6; /** * 5G SS reference signal received quality. - * Range: -20 dB to -3 dB. - * Used RAN: NGRAN - * Reference: 3GPP TS 38.215. + * Range: -43 dB to 20 dB. + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN} + * Reference: 3GPP TS 38.133 section 10.1.11.1. */ - public static final int SIGNAL_SSRSRQ = 7; + public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRQ = 7; /** * 5G SS signal-to-noise and interference ratio. * Range: -23 dB to 40 dB - * Used RAN: NGRAN + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN} * Reference: 3GPP TS 38.215 section 5.1.*, 3GPP TS 38.133 section 10.1.16.1. */ - public static final int SIGNAL_SSSINR = 8; + public static final int SIGNAL_MEASUREMENT_TYPE_SSSINR = 8; /** @hide */ - @IntDef(prefix = { "SIGNAL_" }, value = { - SIGNAL_RSSI, - SIGNAL_RSCP, - SIGNAL_RSRP, - SIGNAL_RSRQ, - SIGNAL_RSSNR, - SIGNAL_SSRSRP, - SIGNAL_SSRSRQ, - SIGNAL_SSSINR + @IntDef(prefix = {"SIGNAL_MEASUREMENT_TYPE_"}, value = { + SIGNAL_MEASUREMENT_TYPE_UNKNOWN, + SIGNAL_MEASUREMENT_TYPE_RSSI, + SIGNAL_MEASUREMENT_TYPE_RSCP, + SIGNAL_MEASUREMENT_TYPE_RSRP, + SIGNAL_MEASUREMENT_TYPE_RSRQ, + SIGNAL_MEASUREMENT_TYPE_RSSNR, + SIGNAL_MEASUREMENT_TYPE_SSRSRP, + SIGNAL_MEASUREMENT_TYPE_SSRSRQ, + SIGNAL_MEASUREMENT_TYPE_SSSINR }) @Retention(RetentionPolicy.SOURCE) - public @interface SignalMeasurementType {} + public @interface SignalMeasurementType { + } @SignalMeasurementType - private int mSignalMeasurement; + private final int mSignalMeasurementType; /** * A hysteresis time in milliseconds to prevent flapping. * A value of 0 disables hysteresis */ - private int mHysteresisMs; + private final int mHysteresisMs; /** * An interval in dB defining the required magnitude change between reports. * hysteresisDb must be smaller than the smallest threshold delta. * An interval value of 0 disables hysteresis. */ - private int mHysteresisDb; + private final int mHysteresisDb; /** * List of threshold values. @@ -130,60 +138,399 @@ public class SignalThresholdInfo implements Parcelable { * The threshold values for which to apply criteria. * A vector size of 0 disables the use of thresholds for reporting. */ - private int[] mThresholds = null; + private final int[] mThresholds; /** * {@code true} means modem must trigger the report based on the criteria; * {@code false} means modem must not trigger the report based on the criteria. */ - private boolean mIsEnabled = true; + private final boolean mIsEnabled; + + /** + * The radio access network type associated with the signal thresholds. + */ + @AccessNetworkConstants.RadioAccessNetworkType + private final int mRan; /** * Indicates the hysteresisMs is disabled. + * + * @hide */ public static final int HYSTERESIS_MS_DISABLED = 0; /** * Indicates the hysteresisDb is disabled. + * + * @hide */ public static final int HYSTERESIS_DB_DISABLED = 0; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSI}. + * + * @hide + */ + public static final int SIGNAL_RSSI_MIN_VALUE = -113; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSI}. + * + * @hide + */ + public static final int SIGNAL_RSSI_MAX_VALUE = -51; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSCP}. + * + * @hide + */ + public static final int SIGNAL_RSCP_MIN_VALUE = -120; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSCP}. + * + * @hide + */ + public static final int SIGNAL_RSCP_MAX_VALUE = -25; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRP}. + * + * @hide + */ + public static final int SIGNAL_RSRP_MIN_VALUE = -140; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRP}. + * + * @hide + */ + public static final int SIGNAL_RSRP_MAX_VALUE = -44; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRQ}. + * + * @hide + */ + public static final int SIGNAL_RSRQ_MIN_VALUE = -34; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRQ}. + * + * @hide + */ + public static final int SIGNAL_RSRQ_MAX_VALUE = 3; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSNR}. + * + * @hide + */ + public static final int SIGNAL_RSSNR_MIN_VALUE = -20; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSNR}. + * + * @hide + */ + public static final int SIGNAL_RSSNR_MAX_VALUE = 30; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRP}. + * + * @hide + */ + public static final int SIGNAL_SSRSRP_MIN_VALUE = -140; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRP}. + * + * @hide + */ + public static final int SIGNAL_SSRSRP_MAX_VALUE = -44; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRQ}. + * + * @hide + */ + public static final int SIGNAL_SSRSRQ_MIN_VALUE = -43; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRQ}. + * + * @hide + */ + public static final int SIGNAL_SSRSRQ_MAX_VALUE = 20; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSSINR}. + * + * @hide + */ + public static final int SIGNAL_SSSINR_MIN_VALUE = -23; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSSINR}. + * + * @hide + */ + 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 signalMeasurement Signal Measurement Type - * @param hysteresisMs hysteresisMs - * @param hysteresisDb hysteresisDb - * @param thresholds threshold value - * @param isEnabled isEnabled - */ - public SignalThresholdInfo(@SignalMeasurementType int signalMeasurement, - int hysteresisMs, int hysteresisDb, @NonNull int [] thresholds, boolean isEnabled) { - mSignalMeasurement = signalMeasurement; + * @param ran Radio Access Network type + * @param signalMeasurementType Signal Measurement Type + * @param hysteresisMs hysteresisMs + * @param hysteresisDb hysteresisDb + * @param thresholds threshold value + * @param isEnabled isEnabled + */ + private SignalThresholdInfo(@AccessNetworkConstants.RadioAccessNetworkType int ran, + @SignalMeasurementType int signalMeasurementType, int hysteresisMs, int hysteresisDb, + @NonNull int[] thresholds, boolean isEnabled) { + Objects.requireNonNull(thresholds, "thresholds must not be null"); + validateRanWithMeasurementType(ran, signalMeasurementType); + validateThresholdRange(signalMeasurementType, thresholds); + + mRan = ran; + mSignalMeasurementType = signalMeasurementType; mHysteresisMs = hysteresisMs < 0 ? HYSTERESIS_MS_DISABLED : hysteresisMs; mHysteresisDb = hysteresisDb < 0 ? HYSTERESIS_DB_DISABLED : hysteresisDb; - mThresholds = thresholds == null ? null : thresholds.clone(); + mThresholds = thresholds; mIsEnabled = isEnabled; } - public @SignalMeasurementType int getSignalMeasurement() { - return mSignalMeasurement; + /** + * Builder class to create {@link SignalThresholdInfo} objects. + */ + public static final class Builder { + private int mRan = AccessNetworkConstants.AccessNetworkType.UNKNOWN; + private int mSignalMeasurementType = SIGNAL_MEASUREMENT_TYPE_UNKNOWN; + private int mHysteresisMs = HYSTERESIS_MS_DISABLED; + private int mHysteresisDb = HYSTERESIS_DB_DISABLED; + private int[] mThresholds = null; + private boolean mIsEnabled = false; + + /** + * Set the radio access network type for the builder instance. + * + * @param ran The radio access network type + * @return the builder to facilitate the chaining + */ + public @NonNull Builder setRadioAccessNetworkType( + @AccessNetworkConstants.RadioAccessNetworkType int ran) { + mRan = ran; + return this; + } + + /** + * Set the signal measurement type for the builder instance. + * + * @param signalMeasurementType The signal measurement type + * @return the builder to facilitate the chaining + */ + public @NonNull Builder setSignalMeasurementType( + @SignalMeasurementType int signalMeasurementType) { + mSignalMeasurementType = signalMeasurementType; + return this; + } + + /** + * Set the hysteresis time in milliseconds to prevent flapping. A value of 0 disables + * hysteresis. + * + * @param hysteresisMs the hysteresis time in milliseconds + * @return the builder to facilitate the chaining + * @hide + */ + public @NonNull Builder setHysteresisMs(int hysteresisMs) { + mHysteresisMs = hysteresisMs; + return this; + } + + /** + * Set the interval in dB defining the required magnitude change between reports. A value of + * zero disabled dB-based hysteresis restrictions. + * + * @param hysteresisDb the interval in dB + * @return the builder to facilitate the chaining + * @hide + */ + public @NonNull Builder setHysteresisDb(int hysteresisDb) { + mHysteresisDb = hysteresisDb; + return this; + } + + /** + * Set the signal strength thresholds of the corresponding signal measurement type. + * + * 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 + * @return the builder to facilitate the chaining + * @hide + */ + public @NonNull Builder setIsEnabled(boolean isEnabled) { + mIsEnabled = isEnabled; + return this; + } + + /** + * Build {@link SignalThresholdInfo} object. + * + * @return the SignalThresholdInfo object build out + * + * @throws IllegalArgumentException if the signal measurement type is invalid, any value in + * the thresholds is out of range, or the RAN is not allowed to set with the signal + * measurement type + */ + public @NonNull SignalThresholdInfo build() { + return new SignalThresholdInfo(mRan, mSignalMeasurementType, mHysteresisMs, + mHysteresisDb, mThresholds, mIsEnabled); + } + } + + /** + * Get the radio access network type. + * + * @return radio access network type + */ + public @AccessNetworkConstants.RadioAccessNetworkType int getRadioAccessNetworkType() { + return mRan; + } + + /** + * Get the signal measurement type. + * + * @return the SignalMeasurementType value + */ + public @SignalMeasurementType int getSignalMeasurementType() { + return mSignalMeasurementType; } + /** @hide */ public int getHysteresisMs() { return mHysteresisMs; } + /** @hide */ public int getHysteresisDb() { return mHysteresisDb; } + /** @hide */ public boolean isEnabled() { return mIsEnabled; } - public int[] getThresholds() { - return mThresholds == null ? null : mThresholds.clone(); + /** + * 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 + * + * @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 @@ -192,8 +539,9 @@ public class SignalThresholdInfo implements Parcelable { } @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(mSignalMeasurement); + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeInt(mRan); + out.writeInt(mSignalMeasurementType); out.writeInt(mHysteresisMs); out.writeInt(mHysteresisDb); out.writeIntArray(mThresholds); @@ -201,7 +549,8 @@ public class SignalThresholdInfo implements Parcelable { } private SignalThresholdInfo(Parcel in) { - mSignalMeasurement = in.readInt(); + mRan = in.readInt(); + mSignalMeasurementType = in.readInt(); mHysteresisMs = in.readInt(); mHysteresisDb = in.readInt(); mThresholds = in.createIntArray(); @@ -217,7 +566,8 @@ public class SignalThresholdInfo implements Parcelable { } SignalThresholdInfo other = (SignalThresholdInfo) o; - return mSignalMeasurement == other.mSignalMeasurement + return mRan == other.mRan + && mSignalMeasurementType == other.mSignalMeasurementType && mHysteresisMs == other.mHysteresisMs && mHysteresisDb == other.mHysteresisDb && Arrays.equals(mThresholds, other.mThresholds) @@ -226,8 +576,8 @@ public class SignalThresholdInfo implements Parcelable { @Override public int hashCode() { - return Objects.hash( - mSignalMeasurement, mHysteresisMs, mHysteresisDb, mThresholds, mIsEnabled); + return Objects.hash(mRan, mSignalMeasurementType, mHysteresisMs, mHysteresisDb, mThresholds, + mIsEnabled); } public static final @NonNull Parcelable.Creator<SignalThresholdInfo> CREATOR = @@ -246,11 +596,83 @@ public class SignalThresholdInfo implements Parcelable { @Override public String toString() { return new StringBuilder("SignalThresholdInfo{") - .append("mSignalMeasurement=").append(mSignalMeasurement) - .append("mHysteresisMs=").append(mSignalMeasurement) - .append("mHysteresisDb=").append(mHysteresisDb) - .append("mThresholds=").append(Arrays.toString(mThresholds)) - .append("mIsEnabled=").append(mIsEnabled) - .append("}").toString(); + .append("mRan=").append(mRan) + .append(" mSignalMeasurementType=").append(mSignalMeasurementType) + .append(" mHysteresisMs=").append(mHysteresisMs) + .append(" mHysteresisDb=").append(mHysteresisDb) + .append(" mThresholds=").append(Arrays.toString(mThresholds)) + .append(" mIsEnabled=").append(mIsEnabled) + .append("}").toString(); + } + + /** + * Return true if signal measurement type is valid and the threshold value is in range. + */ + private static boolean isValidThreshold(@SignalMeasurementType int type, int threshold) { + switch (type) { + case SIGNAL_MEASUREMENT_TYPE_RSSI: + return threshold >= SIGNAL_RSSI_MIN_VALUE && threshold <= SIGNAL_RSSI_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_RSCP: + return threshold >= SIGNAL_RSCP_MIN_VALUE && threshold <= SIGNAL_RSCP_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_RSRP: + return threshold >= SIGNAL_RSRP_MIN_VALUE && threshold <= SIGNAL_RSRP_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_RSRQ: + return threshold >= SIGNAL_RSRQ_MIN_VALUE && threshold <= SIGNAL_RSRQ_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_RSSNR: + return threshold >= SIGNAL_RSSNR_MIN_VALUE && threshold <= SIGNAL_RSSNR_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_SSRSRP: + return threshold >= SIGNAL_SSRSRP_MIN_VALUE && threshold <= SIGNAL_SSRSRP_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_SSRSRQ: + return threshold >= SIGNAL_SSRSRQ_MIN_VALUE && threshold <= SIGNAL_SSRSRQ_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_SSSINR: + return threshold >= SIGNAL_SSSINR_MIN_VALUE && threshold <= SIGNAL_SSSINR_MAX_VALUE; + default: + return false; + } + } + + /** + * Return true if the radio access type is allowed to set with the measurement type. + */ + private static boolean isValidRanWithMeasurementType( + @AccessNetworkConstants.RadioAccessNetworkType int ran, + @SignalMeasurementType int type) { + switch (type) { + case SIGNAL_MEASUREMENT_TYPE_RSSI: + return ran == AccessNetworkConstants.AccessNetworkType.GERAN + || ran == AccessNetworkConstants.AccessNetworkType.CDMA2000; + case SIGNAL_MEASUREMENT_TYPE_RSCP: + return ran == AccessNetworkConstants.AccessNetworkType.UTRAN; + case SIGNAL_MEASUREMENT_TYPE_RSRP: + case SIGNAL_MEASUREMENT_TYPE_RSRQ: + case SIGNAL_MEASUREMENT_TYPE_RSSNR: + return ran == AccessNetworkConstants.AccessNetworkType.EUTRAN; + case SIGNAL_MEASUREMENT_TYPE_SSRSRP: + case SIGNAL_MEASUREMENT_TYPE_SSRSRQ: + case SIGNAL_MEASUREMENT_TYPE_SSSINR: + return ran == AccessNetworkConstants.AccessNetworkType.NGRAN; + default: + return false; + } + } + + private void validateRanWithMeasurementType( + @AccessNetworkConstants.RadioAccessNetworkType int ran, + @SignalMeasurementType int signalMeasurement) { + if (!isValidRanWithMeasurementType(ran, signalMeasurement)) { + throw new IllegalArgumentException( + "invalid RAN: " + ran + " with signal measurement type: " + signalMeasurement); + } + } + + private void validateThresholdRange(@SignalMeasurementType int signalMeasurement, + int[] thresholds) { + for (int threshold : thresholds) { + if (!isValidThreshold(signalMeasurement, threshold)) { + throw new IllegalArgumentException( + "invalid signal measurement type: " + signalMeasurement + + " with threshold: " + threshold); + } + } } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 27025759a317..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; @@ -9045,11 +9047,18 @@ public class TelephonyManager { */ public static final int CALL_COMPOSER_STATUS_ON = 1; + /** + * Call composer status indicating that sending/receiving pictures is disabled. + * All other attachments are still enabled in this state. + */ + public static final int CALL_COMPOSER_STATUS_ON_NO_PICTURES = 2; + /** @hide */ @IntDef(prefix = {"CALL_COMPOSER_STATUS_"}, value = { CALL_COMPOSER_STATUS_ON, CALL_COMPOSER_STATUS_OFF, + CALL_COMPOSER_STATUS_ON_NO_PICTURES, }) public @interface CallComposerStatus {} @@ -9057,8 +9066,9 @@ public class TelephonyManager { * Set the user-set status for enriched calling with call composer. * * @param status user-set status for enriched calling with call composer; - * it must be a value of either {@link #CALL_COMPOSER_STATUS_ON} - * or {@link #CALL_COMPOSER_STATUS_OFF}. + * it must be any of {@link #CALL_COMPOSER_STATUS_ON} + * {@link #CALL_COMPOSER_STATUS_OFF}, + * or {@link #CALL_COMPOSER_STATUS_ON_NO_PICTURES} * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} @@ -9068,7 +9078,8 @@ public class TelephonyManager { */ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallComposerStatus(@CallComposerStatus int status) { - if (status != CALL_COMPOSER_STATUS_ON && status != CALL_COMPOSER_STATUS_OFF) { + if (status > CALL_COMPOSER_STATUS_ON_NO_PICTURES + || status < CALL_COMPOSER_STATUS_OFF) { throw new IllegalArgumentException("requested status is invalid"); } try { @@ -9090,8 +9101,9 @@ public class TelephonyManager { * * @throws SecurityException if the caller does not have the permission. * - * @return the user-set status for enriched calling with call composer either - * {@link #CALL_COMPOSER_STATUS_ON} or {@link #CALL_COMPOSER_STATUS_OFF}. + * @return the user-set status for enriched calling with call composer, any of + * {@link #CALL_COMPOSER_STATUS_ON}, {@link #CALL_COMPOSER_STATUS_OFF}, or + * {@link #CALL_COMPOSER_STATUS_ON_NO_PICTURES}. */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @CallComposerStatus int getCallComposerStatus() { @@ -15175,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/telephony/java/android/telephony/ims/DelegateRegistrationState.java b/telephony/java/android/telephony/ims/DelegateRegistrationState.java index 66281edc0de1..fd206c1e803f 100644 --- a/telephony/java/android/telephony/ims/DelegateRegistrationState.java +++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.java @@ -320,4 +320,11 @@ public final class DelegateRegistrationState implements Parcelable { public int hashCode() { return Objects.hash(mRegisteredTags, mDeregisteringTags, mDeregisteredTags); } + + @Override + public String toString() { + return "DelegateRegistrationState{ registered={" + mRegisteredTags + + "}, deregistering={" + mDeregisteringTags + "}, deregistered={" + + mDeregisteredTags + "}}"; + } } diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java index 1539224dedcf..006cca84e44b 100644 --- a/telephony/java/android/telephony/ims/SipMessage.java +++ b/telephony/java/android/telephony/ims/SipMessage.java @@ -22,6 +22,8 @@ import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.telephony.SipMessageParsingUtils; + import java.util.Arrays; import java.util.Objects; @@ -38,9 +40,6 @@ public final class SipMessage implements Parcelable { // Should not be set to true for production! private static final boolean IS_DEBUGGING = Build.IS_ENG; - private static final String[] SIP_REQUEST_METHODS = new String[] {"INVITE", "ACK", "OPTIONS", - "BYE", "CANCEL", "REGISTER"}; - private final String mStartLine; private final String mHeaderSection; private final byte[] mContent; @@ -72,6 +71,7 @@ public final class SipMessage implements Parcelable { mContent = new byte[source.readInt()]; source.readByteArray(mContent); } + /** * @return The start line of the SIP message, which contains either the request-line or * status-line. @@ -128,34 +128,25 @@ public final class SipMessage implements Parcelable { } else { b.append(sanitizeStartLineRequest(mStartLine)); } - b.append("], ["); - b.append("Header: ["); + b.append("], Header: ["); if (IS_DEBUGGING) { b.append(mHeaderSection); } else { // only identify transaction id/call ID when it is available. b.append("***"); } - b.append("], "); - b.append("Content: [NOT SHOWN]"); + b.append("], Content: "); + b.append(getContent().length == 0 ? "[NONE]" : "[NOT SHOWN]"); return b.toString(); } /** - * Start lines containing requests are formatted: METHOD SP Request-URI SP SIP-Version CRLF. * Detect if this is a REQUEST and redact Request-URI portion here, as it contains PII. */ private String sanitizeStartLineRequest(String startLine) { + if (!SipMessageParsingUtils.isSipRequest(startLine)) return startLine; String[] splitLine = startLine.split(" "); - if (splitLine == null || splitLine.length == 0) { - return "(INVALID STARTLINE)"; - } - for (String method : SIP_REQUEST_METHODS) { - if (splitLine[0].contains(method)) { - return splitLine[0] + " <Request-URI> " + splitLine[2]; - } - } - return startLine; + return splitLine[0] + " <Request-URI> " + splitLine[2]; } @Override diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java index 522ad8160870..9d919015087d 100644 --- a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java +++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java @@ -28,6 +28,10 @@ import android.telephony.ims.SipDelegateImsConfiguration; import android.telephony.ims.SipDelegateManager; import android.telephony.ims.SipMessage; import android.telephony.ims.stub.SipDelegate; +import android.text.TextUtils; +import android.util.Log; + +import com.android.internal.telephony.SipMessageParsingUtils; import java.util.ArrayList; import java.util.Set; @@ -40,6 +44,7 @@ import java.util.concurrent.Executor; * @hide */ public class SipDelegateAidlWrapper implements DelegateStateCallback, DelegateMessageCallback { + private static final String LOG_TAG = "SipDelegateAW"; private final ISipDelegate.Stub mDelegateBinder = new ISipDelegate.Stub() { @Override @@ -183,11 +188,15 @@ public class SipDelegateAidlWrapper implements DelegateStateCallback, DelegateMe } private void notifyLocalMessageFailedToBeReceived(SipMessage m, int reason) { - //TODO: parse transaction ID or throw IllegalArgumentException if the SipMessage - // transaction ID can not be parsed. + String transactionId = SipMessageParsingUtils.getTransactionId(m.getHeaderSection()); + if (TextUtils.isEmpty(transactionId)) { + Log.w(LOG_TAG, "failure to parse SipMessage."); + throw new IllegalArgumentException("Malformed SipMessage, can not determine " + + "transaction ID."); + } SipDelegate d = mDelegate; if (d != null) { - mExecutor.execute(() -> d.notifyMessageReceiveError(null, reason)); + mExecutor.execute(() -> d.notifyMessageReceiveError(transactionId, reason)); } } } diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java index a35039bd7668..c877aca8ba96 100644 --- a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java +++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java @@ -28,9 +28,12 @@ import android.telephony.ims.SipMessage; import android.telephony.ims.stub.DelegateConnectionMessageCallback; import android.telephony.ims.stub.DelegateConnectionStateCallback; import android.telephony.ims.stub.SipDelegate; +import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; +import com.android.internal.telephony.SipMessageParsingUtils; + import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.Executor; @@ -265,9 +268,13 @@ public class SipDelegateConnectionAidlWrapper implements SipDelegateConnection, } private void notifyLocalMessageFailedToSend(SipMessage m, int reason) { - //TODO: parse transaction ID or throw IllegalArgumentException if the SipMessage - // transaction ID can not be parsed. + String transactionId = SipMessageParsingUtils.getTransactionId(m.getHeaderSection()); + if (TextUtils.isEmpty(transactionId)) { + Log.w(LOG_TAG, "sendMessage detected a malformed SipMessage and can not get a " + + "transaction ID."); + throw new IllegalArgumentException("Could not send SipMessage due to malformed header"); + } mExecutor.execute(() -> - mMessageCallback.onMessageSendFailure(null, reason)); + mMessageCallback.onMessageSendFailure(transactionId, reason)); } } diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java index d2cb9761a028..d9734a7475c0 100644 --- a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java +++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java @@ -18,15 +18,12 @@ package android.telephony.ims.stub; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.net.Uri; import android.telephony.ims.ImsException; import android.telephony.ims.RcsContactUceCapability; import android.telephony.ims.RcsUceAdapter; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.feature.RcsFeature; -import java.util.List; - /** * The interface of the capabilities event listener for ImsService to notify the framework of the * UCE request and status updated. @@ -84,25 +81,4 @@ public interface CapabilityExchangeEventListener { * Telephony stack has crashed. */ void onUnpublish() throws ImsException; - - /** - * Inform the framework of a query for this device's UCE capabilities. - * <p> - * The framework will respond via the - * {@link OptionsRequestCallback#onRespondToCapabilityRequest} or - * {@link OptionsRequestCallback#onRespondToCapabilityRequestWithError} - * @param contactUri The URI associated with the remote contact that is - * requesting capabilities. - * @param remoteCapabilities The remote contact's capability information. - * @param callback The callback of this request which is sent from the remote user. - * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not - * currently connected to the framework. This can happen if the {@link RcsFeature} is not - * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received - * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare - * cases when the Telephony stack has crashed. - * @hide - */ - void onRemoteCapabilityRequest(@NonNull Uri contactUri, - @NonNull List<String> remoteCapabilities, - @NonNull OptionsRequestCallback callback) throws ImsException; } 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/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java index 2ebb9c13a016..94950dc456ca 100644 --- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java +++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java @@ -16,11 +16,11 @@ package com.android.tests.rollback.host; +import static com.android.tests.rollback.host.WatchdogEventLogger.Subject.assertThat; + import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; @@ -62,9 +62,9 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { * For example, <code>runPhase("testApkOnlyEnableRollback");</code> */ private void runPhase(String phase) throws Exception { - assertTrue(runDeviceTests("com.android.tests.rollback", + assertThat(runDeviceTests("com.android.tests.rollback", "com.android.tests.rollback.StagedRollbackTest", - phase)); + phase)).isTrue(); } private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test"; @@ -150,17 +150,15 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { // Trigger rollback and wait for reboot to happen runPhase("testBadApkOnly_Phase3"); - assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(2))); + assertThat(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(2))).isTrue(); getDevice().waitForDeviceAvailable(); runPhase("testBadApkOnly_Phase4"); - assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_INITIATE, null, - REASON_APP_CRASH, TESTAPP_A)); - assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_BOOT_TRIGGERED, null, - null, null)); - assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_SUCCESS, null, null, null)); + assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_APP_CRASH, TESTAPP_A); + assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null); + assertThat(mLogger).eventOccurred(ROLLBACK_SUCCESS, null, null, null); } @Test @@ -183,17 +181,15 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { // 3. Staged rollback session becomes ready. // 4. Device actually reboots. // So we give a generous timeout here. - assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5))); + assertThat(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5))).isTrue(); getDevice().waitForDeviceAvailable(); // verify rollback committed runPhase("testNativeWatchdogTriggersRollback_Phase3"); - assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_INITIATE, null, - REASON_NATIVE_CRASH, null)); - assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_BOOT_TRIGGERED, null, - null, null)); - assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_SUCCESS, null, null, null)); + assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_NATIVE_CRASH, null); + assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null); + assertThat(mLogger).eventOccurred(ROLLBACK_SUCCESS, null, null, null); } @Test @@ -223,17 +219,15 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { // 3. Staged rollback session becomes ready. // 4. Device actually reboots. // So we give a generous timeout here. - assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5))); + assertThat(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5))).isTrue(); getDevice().waitForDeviceAvailable(); // verify all available rollbacks have been committed runPhase("testNativeWatchdogTriggersRollbackForAll_Phase4"); - assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_INITIATE, null, - REASON_NATIVE_CRASH, null)); - assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_BOOT_TRIGGERED, null, - null, null)); - assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_SUCCESS, null, null, null)); + assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_NATIVE_CRASH, null); + assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null); + assertThat(mLogger).eventOccurred(ROLLBACK_SUCCESS, null, null, null); } /** @@ -306,16 +300,14 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { // Verify apex was installed and then crash the apk runPhase("testRollbackApexWithApkCrashing_Phase2"); // Wait for crash to trigger rollback - assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5))); + assertThat(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5))).isTrue(); getDevice().waitForDeviceAvailable(); // Verify rollback occurred due to crash of apk-in-apex runPhase("testRollbackApexWithApkCrashing_Phase3"); - assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_INITIATE, null, - REASON_APP_CRASH, TESTAPP_A)); - assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_BOOT_TRIGGERED, null, - null, null)); - assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_SUCCESS, null, null, null)); + assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_APP_CRASH, TESTAPP_A); + assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null); + assertThat(mLogger).eventOccurred(ROLLBACK_SUCCESS, null, null, null); } /** @@ -356,10 +348,10 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { // Verify that old files have been restored and new files are gone runAsRoot(() -> { - assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1)); - assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2)); - assertNull(getDevice().pullFile(newFilePath3)); - assertNull(getDevice().pullFile(newFilePath4)); + assertThat(getDevice().pullFileContents(oldFilePath1)).isEqualTo(TEST_STRING_1); + assertThat(getDevice().pullFileContents(oldFilePath2)).isEqualTo(TEST_STRING_2); + assertThat(getDevice().pullFile(newFilePath3)).isNull(); + assertThat(getDevice().pullFile(newFilePath4)).isNull(); }); // Verify snapshots are deleted after restoration @@ -411,10 +403,10 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { // Verify that old files have been restored and new files are gone runAsRoot(() -> { - assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1)); - assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2)); - assertNull(getDevice().pullFile(newFilePath3)); - assertNull(getDevice().pullFile(newFilePath4)); + assertThat(getDevice().pullFileContents(oldFilePath1)).isEqualTo(TEST_STRING_1); + assertThat(getDevice().pullFileContents(oldFilePath2)).isEqualTo(TEST_STRING_2); + assertThat(getDevice().pullFile(newFilePath3)).isNull(); + assertThat(getDevice().pullFile(newFilePath4)).isNull(); }); // Verify snapshots are deleted after restoration @@ -464,10 +456,10 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { // Verify that old files have been restored and new files are gone runAsRoot(() -> { - assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1)); - assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2)); - assertNull(getDevice().pullFile(newFilePath3)); - assertNull(getDevice().pullFile(newFilePath4)); + assertThat(getDevice().pullFileContents(oldFilePath1)).isEqualTo(TEST_STRING_1); + assertThat(getDevice().pullFileContents(oldFilePath2)).isEqualTo(TEST_STRING_2); + assertThat(getDevice().pullFile(newFilePath3)).isNull(); + assertThat(getDevice().pullFile(newFilePath4)).isNull(); }); // Verify snapshots are deleted after restoration @@ -515,10 +507,10 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { // Verify that old files have been restored and new files are gone runAsRoot(() -> { - assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1)); - assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2)); - assertNull(getDevice().pullFile(newFilePath3)); - assertNull(getDevice().pullFile(newFilePath4)); + assertThat(getDevice().pullFileContents(oldFilePath1)).isEqualTo(TEST_STRING_1); + assertThat(getDevice().pullFileContents(oldFilePath2)).isEqualTo(TEST_STRING_2); + assertThat(getDevice().pullFile(newFilePath3)).isNull(); + assertThat(getDevice().pullFile(newFilePath4)).isNull(); }); } @@ -549,7 +541,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { runPhase("testCleanUp"); runAsRoot(() -> { for (String dir : after) { - assertNull(getDevice().getFileEntry(dir)); + assertThat(getDevice().getFileEntry(dir)).isNull(); } }); } @@ -561,7 +553,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { try { getDevice().enableAdbRoot(); getDevice().remountSystemWritable(); - assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName)); + assertThat(getDevice().pushFile(apex, "/system/apex/" + fileName)).isTrue(); } finally { getDevice().disableAdbRoot(); } @@ -607,8 +599,9 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { try { getDevice().enableAdbRoot(); IFileEntry file = getDevice().getFileEntry(path); - assertTrue("Not a directory: " + path, file.isDirectory()); - assertTrue("Directory not empty: " + path, file.getChildren(false).isEmpty()); + assertWithMessage("Not a directory: " + path).that(file.isDirectory()).isTrue(); + assertWithMessage("Directory not empty: " + path) + .that(file.getChildren(false)).isEmpty(); } catch (DeviceNotAvailableException e) { fail("Can't access directory: " + path); } finally { diff --git a/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java b/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java index 6b0d1f860956..8c16079dca85 100644 --- a/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java +++ b/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java @@ -17,6 +17,8 @@ package com.android.tests.rollback.host; import com.android.tradefed.device.ITestDevice; +import com.google.common.truth.FailureMetadata; +import com.google.common.truth.Truth; import static com.google.common.truth.Truth.assertThat; @@ -76,4 +78,30 @@ public class WatchdogEventLogger { && matchProperty(type, "rollbackReason", rollbackReason) && matchProperty(type, "failedPackageName", failedPackageName); } + + static class Subject extends com.google.common.truth.Subject { + private final WatchdogEventLogger mActual; + + private Subject(FailureMetadata failureMetadata, WatchdogEventLogger subject) { + super(failureMetadata, subject); + mActual = subject; + } + + private static com.google.common.truth.Subject.Factory<Subject, + WatchdogEventLogger> loggers() { + return Subject::new; + } + + static Subject assertThat(WatchdogEventLogger actual) { + return Truth.assertAbout(loggers()).that(actual); + } + + void eventOccurred(String type, String logPackage, String rollbackReason, + String failedPackageName) throws Exception { + check("watchdogEventOccurred(type=%s, logPackage=%s, rollbackReason=%s, " + + "failedPackageName=%s)", type, logPackage, rollbackReason, failedPackageName) + .that(mActual.watchdogEventOccurred(type, logPackage, rollbackReason, + failedPackageName)).isTrue(); + } + } } diff --git a/tests/StagedInstallTest/OWNERS b/tests/StagedInstallTest/OWNERS index d825dfd7cf00..aac68e994a39 100644 --- a/tests/StagedInstallTest/OWNERS +++ b/tests/StagedInstallTest/OWNERS @@ -1 +1,5 @@ include /services/core/java/com/android/server/pm/OWNERS + +dariofreni@google.com +ioffe@google.com +olilan@google.com 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 8710d23730b6..b2bcfeb9019d 100644 --- a/tests/net/common/java/android/net/CaptivePortalDataTest.kt +++ b/tests/net/common/java/android/net/CaptivePortalDataTest.kt @@ -18,12 +18,15 @@ package android.net import android.os.Build import androidx.test.filters.SmallTest +import com.android.modules.utils.build.SdkLevel import com.android.testutils.assertParcelSane import com.android.testutils.assertParcelingIsLossless +import com.android.testutils.DevSdkIgnoreRule import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo import com.android.testutils.DevSdkIgnoreRunner import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import kotlin.test.assertEquals @@ -33,6 +36,9 @@ import kotlin.test.assertNotEquals @RunWith(DevSdkIgnoreRunner::class) @IgnoreUpTo(Build.VERSION_CODES.Q) class CaptivePortalDataTest { + @Rule @JvmField + val ignoreRule = DevSdkIgnoreRule() + private val data = CaptivePortalData.Builder() .setRefreshTime(123L) .setUserPortalUrl(Uri.parse("https://portal.example.com/test")) @@ -41,14 +47,19 @@ class CaptivePortalDataTest { .setBytesRemaining(456L) .setExpiryTime(789L) .setCaptive(true) - .setVenueFriendlyName("venue friendly name") + .apply { + if (SdkLevel.isAtLeastS()) { + setVenueFriendlyName("venue friendly name") + } + } .build() private fun makeBuilder() = CaptivePortalData.Builder(data) @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()) @@ -67,8 +78,11 @@ class CaptivePortalDataTest { assertNotEqualsAfterChange { it.setBytesRemaining(789L) } assertNotEqualsAfterChange { it.setExpiryTime(12L) } assertNotEqualsAfterChange { it.setCaptive(false) } - assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") } - assertNotEqualsAfterChange { it.setVenueFriendlyName(null) } + + if (SdkLevel.isAtLeastS()) { + assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") } + assertNotEqualsAfterChange { it.setVenueFriendlyName(null) } + } } @Test @@ -111,7 +125,7 @@ class CaptivePortalDataTest { assertFalse(makeBuilder().setCaptive(false).build().isCaptive) } - @Test + @Test @IgnoreUpTo(Build.VERSION_CODES.R) fun testVenueFriendlyName() { assertEquals("venue friendly name", data.venueFriendlyName) } 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 6953100dd2ac..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; @@ -290,13 +292,16 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -364,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; @@ -385,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); @@ -411,9 +415,6 @@ public class ConnectivityServiceTest { private class MockContext extends BroadcastInterceptingContext { private final MockContentResolver mContentResolver; - // Contains all registered receivers since this object was created. Useful to clear - // them when needed, as BroadcastInterceptingContext does not provide this facility. - private final List<BroadcastReceiver> mRegisteredReceivers = new ArrayList<>(); @Spy private Resources mResources; private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>(); @@ -550,19 +551,6 @@ public class ConnectivityServiceTest { public void setPermission(String permission, Integer granted) { mMockedPermissions.put(permission, granted); } - - @Override - public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { - mRegisteredReceivers.add(receiver); - return super.registerReceiver(receiver, filter); - } - - public void clearRegisteredReceivers() { - // super.unregisterReceiver is a no-op for receivers that are not registered (because - // they haven't been registered or because they have already been unregistered). - // For the same reason, don't bother clearing mRegisteredReceivers. - for (final BroadcastReceiver rcv : mRegisteredReceivers) unregisterReceiver(rcv); - } } private void waitForIdle() { @@ -591,10 +579,10 @@ public class ConnectivityServiceTest { } // Bring up a network that we can use to send messages to ConnectivityService. - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); - waitFor(cv); + b.expectBroadcast(); Network n = mWiFiNetworkAgent.getNetwork(); assertNotNull(n); @@ -611,10 +599,10 @@ public class ConnectivityServiceTest { @Ignore public void verifyThatNotWaitingForIdleCausesRaceConditions() throws Exception { // Bring up a network that we can use to send messages to ConnectivityService. - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); - waitFor(cv); + b.expectBroadcast(); Network n = mWiFiNetworkAgent.getNetwork(); assertNotNull(n); @@ -1079,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() { @@ -1092,7 +1089,7 @@ public class ConnectivityServiceTest { return mDeviceIdleInternal; } }, - mNetworkManagementService, mMockNetd, userId, mock(KeyStore.class)); + mNetworkManagementService, mMockNetd, userId, mKeyStore); } public void setUids(Set<UidRange> uids) { @@ -1204,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; @@ -1288,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 { @@ -1300,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())) @@ -1375,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)); @@ -1514,29 +1556,79 @@ public class ConnectivityServiceTest { } /** - * Return a ConditionVariable that opens when {@code count} numbers of CONNECTIVITY_ACTION - * broadcasts are received. + * Class to simplify expecting broadcasts using BroadcastInterceptingContext. + * Ensures that the receiver is unregistered after the expected broadcast is received. This + * cannot be done in the BroadcastReceiver itself because BroadcastInterceptingContext runs + * the receivers' receive method while iterating over the list of receivers, and unregistering + * the receiver during iteration throws ConcurrentModificationException. */ - private ConditionVariable registerConnectivityBroadcast(final int count) { + private class ExpectedBroadcast extends CompletableFuture<Intent> { + private final BroadcastReceiver mReceiver; + + ExpectedBroadcast(BroadcastReceiver receiver) { + mReceiver = receiver; + } + + public Intent expectBroadcast(int timeoutMs) throws Exception { + try { + return get(timeoutMs, TimeUnit.MILLISECONDS); + } catch (TimeoutException e) { + fail("Expected broadcast not received after " + timeoutMs + " ms"); + return null; + } finally { + mServiceContext.unregisterReceiver(mReceiver); + } + } + + public Intent expectBroadcast() throws Exception { + return expectBroadcast(TIMEOUT_MS); + } + + public void expectNoBroadcast(int timeoutMs) throws Exception { + waitForIdle(); + try { + final Intent intent = get(timeoutMs, TimeUnit.MILLISECONDS); + fail("Unexpected broadcast: " + intent.getAction() + " " + intent.getExtras()); + } catch (TimeoutException expected) { + } finally { + mServiceContext.unregisterReceiver(mReceiver); + } + } + } + + /** Expects that {@code count} CONNECTIVITY_ACTION broadcasts are received. */ + private ExpectedBroadcast registerConnectivityBroadcast(final int count) { return registerConnectivityBroadcastThat(count, intent -> true); } - private ConditionVariable registerConnectivityBroadcastThat(final int count, + private ExpectedBroadcast registerConnectivityBroadcastThat(final int count, @NonNull final Predicate<Intent> filter) { - final ConditionVariable cv = new ConditionVariable(); final IntentFilter intentFilter = new IntentFilter(CONNECTIVITY_ACTION); + // AtomicReference allows receiver to access expected even though it is constructed later. + final AtomicReference<ExpectedBroadcast> expectedRef = new AtomicReference<>(); final BroadcastReceiver receiver = new BroadcastReceiver() { - private int remaining = count; - public void onReceive(Context context, Intent intent) { - if (!filter.test(intent)) return; - if (--remaining == 0) { - cv.open(); - mServiceContext.unregisterReceiver(this); - } - } - }; + private int mRemaining = count; + public void onReceive(Context context, Intent intent) { + final int type = intent.getIntExtra(EXTRA_NETWORK_TYPE, -1); + final NetworkInfo ni = intent.getParcelableExtra(EXTRA_NETWORK_INFO); + Log.d(TAG, "Received CONNECTIVITY_ACTION type=" + type + " ni=" + ni); + if (!filter.test(intent)) return; + if (--mRemaining == 0) { + expectedRef.get().complete(intent); + } + } + }; + final ExpectedBroadcast expected = new ExpectedBroadcast(receiver); + expectedRef.set(expected); mServiceContext.registerReceiver(receiver, intentFilter); - return cv; + return expected; + } + + private ExpectedBroadcast expectConnectivityAction(int type, NetworkInfo.DetailedState state) { + return registerConnectivityBroadcastThat(1, intent -> + type == intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) && state.equals( + ((NetworkInfo) intent.getParcelableExtra(EXTRA_NETWORK_INFO)) + .getDetailedState())); } @Test @@ -1560,10 +1652,9 @@ public class ConnectivityServiceTest { // Connect the cell agent and wait for the connected broadcast. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.addCapability(NET_CAPABILITY_SUPL); - final ConditionVariable cv1 = registerConnectivityBroadcastThat(1, - intent -> intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE); + ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED); mCellNetworkAgent.connect(true); - waitFor(cv1); + b.expectBroadcast(); // Build legacy request for SUPL. final NetworkCapabilities legacyCaps = new NetworkCapabilities(); @@ -1573,27 +1664,17 @@ public class ConnectivityServiceTest { ConnectivityManager.REQUEST_ID_UNSET, NetworkRequest.Type.REQUEST); // File request, withdraw it and make sure no broadcast is sent - final ConditionVariable cv2 = registerConnectivityBroadcast(1); + b = registerConnectivityBroadcast(1); final TestNetworkCallback callback = new TestNetworkCallback(); mCm.requestNetwork(legacyRequest, callback); callback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent); mCm.unregisterNetworkCallback(callback); - assertFalse(cv2.block(800)); // 800ms long enough to at least flake if this is sent - // As the broadcast did not fire, the receiver was not unregistered. Do this now. - mServiceContext.clearRegisteredReceivers(); - - // Disconnect the network and expect mobile disconnected broadcast. Use a small hack to - // check that has been sent. - final AtomicBoolean vanillaAction = new AtomicBoolean(false); - final ConditionVariable cv3 = registerConnectivityBroadcastThat(1, intent -> { - if (intent.getAction().equals(CONNECTIVITY_ACTION)) { - vanillaAction.set(true); - } - return !((NetworkInfo) intent.getExtra(EXTRA_NETWORK_INFO, -1)).isConnected(); - }); + b.expectNoBroadcast(800); // 800ms long enough to at least flake if this is sent + + // Disconnect the network and expect mobile disconnected broadcast. + b = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED); mCellNetworkAgent.disconnect(); - waitFor(cv3); - assertTrue(vanillaAction.get()); + b.expectBroadcast(); } @Test @@ -1604,9 +1685,9 @@ public class ConnectivityServiceTest { assertNull(mCm.getActiveNetworkInfo()); assertNull(mCm.getActiveNetwork()); // Test bringing up validated cellular. - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED); mCellNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); assertLength(2, mCm.getAllNetworks()); assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) || @@ -1614,9 +1695,9 @@ public class ConnectivityServiceTest { assertTrue(mCm.getAllNetworks()[0].equals(mWiFiNetworkAgent.getNetwork()) || mCm.getAllNetworks()[1].equals(mWiFiNetworkAgent.getNetwork())); // Test bringing up validated WiFi. - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mWiFiNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); assertLength(2, mCm.getAllNetworks()); assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) || @@ -1631,9 +1712,9 @@ public class ConnectivityServiceTest { assertLength(1, mCm.getAllNetworks()); assertEquals(mCm.getAllNetworks()[0], mCm.getActiveNetwork()); // Test WiFi disconnect. - cv = registerConnectivityBroadcast(1); + b = registerConnectivityBroadcast(1); mWiFiNetworkAgent.disconnect(); - waitFor(cv); + b.expectBroadcast(); verifyNoNetwork(); } @@ -1641,9 +1722,9 @@ public class ConnectivityServiceTest { public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception { // Test bringing up unvalidated WiFi mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = registerConnectivityBroadcast(1); mWiFiNetworkAgent.connect(false); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); // Test bringing up unvalidated cellular mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); @@ -1656,19 +1737,19 @@ public class ConnectivityServiceTest { verifyActiveNetwork(TRANSPORT_WIFI); // Test bringing up validated cellular mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mCellNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test cellular disconnect. - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mCellNetworkAgent.disconnect(); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); // Test WiFi disconnect. - cv = registerConnectivityBroadcast(1); + b = registerConnectivityBroadcast(1); mWiFiNetworkAgent.disconnect(); - waitFor(cv); + b.expectBroadcast(); verifyNoNetwork(); } @@ -1676,25 +1757,25 @@ public class ConnectivityServiceTest { public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception { // Test bringing up unvalidated cellular. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = registerConnectivityBroadcast(1); mCellNetworkAgent.connect(false); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test bringing up unvalidated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mWiFiNetworkAgent.connect(false); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); // Test WiFi disconnect. - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mWiFiNetworkAgent.disconnect(); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test cellular disconnect. - cv = registerConnectivityBroadcast(1); + b = registerConnectivityBroadcast(1); mCellNetworkAgent.disconnect(); - waitFor(cv); + b.expectBroadcast(); verifyNoNetwork(); } @@ -1702,24 +1783,24 @@ public class ConnectivityServiceTest { public void testUnlingeringDoesNotValidate() throws Exception { // Test bringing up unvalidated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = registerConnectivityBroadcast(1); mWiFiNetworkAgent.connect(false); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); // Test bringing up validated cellular. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mCellNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); // Test cellular disconnect. - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mCellNetworkAgent.disconnect(); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); // Unlingering a network should not cause it to be marked as validated. assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( @@ -1730,25 +1811,25 @@ public class ConnectivityServiceTest { public void testCellularOutscoresWeakWifi() throws Exception { // Test bringing up validated cellular. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = registerConnectivityBroadcast(1); mCellNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test bringing up validated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mWiFiNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); // Test WiFi getting really weak. - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mWiFiNetworkAgent.adjustScore(-11); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test WiFi restoring signal strength. - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mWiFiNetworkAgent.adjustScore(11); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); } @@ -1766,9 +1847,9 @@ public class ConnectivityServiceTest { mCellNetworkAgent.expectDisconnected(); // Test bringing up validated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - final ConditionVariable cv = registerConnectivityBroadcast(1); + final ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); mWiFiNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); // Test bringing up unvalidated cellular. // Expect it to be torn down because it could never be the highest scoring network @@ -1785,33 +1866,33 @@ public class ConnectivityServiceTest { public void testCellularFallback() throws Exception { // Test bringing up validated cellular. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = registerConnectivityBroadcast(1); mCellNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test bringing up validated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mWiFiNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); // Reevaluate WiFi (it'll instantly fail DNS). - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); assertTrue(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); mCm.reportBadNetwork(mWiFiNetworkAgent.getNetwork()); // Should quickly fall back to Cellular. - waitFor(cv); + b.expectBroadcast(); assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); verifyActiveNetwork(TRANSPORT_CELLULAR); // Reevaluate cellular (it'll instantly fail DNS). - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); mCm.reportBadNetwork(mCellNetworkAgent.getNetwork()); // Should quickly fall back to WiFi. - waitFor(cv); + b.expectBroadcast(); assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( @@ -1823,23 +1904,23 @@ public class ConnectivityServiceTest { public void testWiFiFallback() throws Exception { // Test bringing up unvalidated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = registerConnectivityBroadcast(1); mWiFiNetworkAgent.connect(false); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); // Test bringing up validated cellular. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mCellNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); // Reevaluate cellular (it'll instantly fail DNS). - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); mCm.reportBadNetwork(mCellNetworkAgent.getNetwork()); // Should quickly fall back to WiFi. - waitFor(cv); + b.expectBroadcast(); assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); verifyActiveNetwork(TRANSPORT_WIFI); @@ -1909,13 +1990,13 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); // Test unvalidated networks - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = registerConnectivityBroadcast(1); mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(false); genericNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); cellNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - waitFor(cv); + b.expectBroadcast(); assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); // This should not trigger spurious onAvailable() callbacks, b/21762680. @@ -1924,28 +2005,28 @@ public class ConnectivityServiceTest { assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - waitFor(cv); + b.expectBroadcast(); assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mWiFiNetworkAgent.disconnect(); genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); cellNetworkCallback.assertNoCallback(); - waitFor(cv); + b.expectBroadcast(); assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); - cv = registerConnectivityBroadcast(1); + b = registerConnectivityBroadcast(1); mCellNetworkAgent.disconnect(); genericNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - waitFor(cv); + b.expectBroadcast(); assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); // Test validated networks @@ -2048,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); @@ -2071,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()); @@ -2666,9 +2747,9 @@ public class ConnectivityServiceTest { // Test bringing up validated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - final ConditionVariable cv = registerConnectivityBroadcast(1); + final ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); mWiFiNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); // Register MMS NetworkRequest @@ -2694,9 +2775,9 @@ public class ConnectivityServiceTest { public void testMMSonCell() throws Exception { // Test bringing up cellular without MMS mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED); mCellNetworkAgent.connect(false); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); // Register MMS NetworkRequest @@ -4304,9 +4385,9 @@ public class ConnectivityServiceTest { } mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); mWiFiNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); mWiFiNetworkAgent.sendLinkProperties(lp); waitForIdle(); @@ -4862,10 +4943,10 @@ public class ConnectivityServiceTest { assertNotPinnedToWifi(); // Disconnect cell and wifi. - ConditionVariable cv = registerConnectivityBroadcast(3); // cell down, wifi up, wifi down. + ExpectedBroadcast b = registerConnectivityBroadcast(3); // cell down, wifi up, wifi down. mCellNetworkAgent.disconnect(); mWiFiNetworkAgent.disconnect(); - waitFor(cv); + b.expectBroadcast(); // Pinning takes effect even if the pinned network is the default when the pin is set... TestNetworkPinner.pin(mServiceContext, wifiRequest); @@ -4875,10 +4956,10 @@ public class ConnectivityServiceTest { assertPinnedToWifiWithWifiDefault(); // ... and is maintained even when that network is no longer the default. - cv = registerConnectivityBroadcast(1); + b = registerConnectivityBroadcast(1); mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mCellNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); assertPinnedToWifiWithCellDefault(); } @@ -4978,7 +5059,7 @@ public class ConnectivityServiceTest { @Test public void testNetworkInfoOfTypeNone() throws Exception { - ConditionVariable broadcastCV = registerConnectivityBroadcast(1); + ExpectedBroadcast b = registerConnectivityBroadcast(1); verifyNoNetwork(); TestNetworkAgentWrapper wifiAware = new TestNetworkAgentWrapper(TRANSPORT_WIFI_AWARE); @@ -5011,9 +5092,7 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(callback); verifyNoNetwork(); - if (broadcastCV.block(10)) { - fail("expected no broadcast, but got CONNECTIVITY_ACTION broadcast"); - } + b.expectNoBroadcast(10); } @Test @@ -5813,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(); @@ -6291,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); @@ -6323,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. @@ -6347,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)); @@ -6357,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 @@ -6373,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(); } @@ -6760,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); @@ -6781,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); @@ -6827,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); @@ -6907,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() @@ -7250,11 +7662,11 @@ public class ConnectivityServiceTest { // prefix discovery is never started. LinkProperties lp = new LinkProperties(baseLp); lp.setNat64Prefix(pref64FromRa); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp); - mCellNetworkAgent.connect(false); - final Network network = mCellNetworkAgent.getNetwork(); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp); + mWiFiNetworkAgent.connect(false); + final Network network = mWiFiNetworkAgent.getNetwork(); int netId = network.getNetId(); - callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString()); inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString()); inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId); @@ -7263,8 +7675,8 @@ public class ConnectivityServiceTest { // If the RA prefix is withdrawn, clatd is stopped and prefix discovery is started. lp.setNat64Prefix(null); - mCellNetworkAgent.sendLinkProperties(lp); - expectNat64PrefixChange(callback, mCellNetworkAgent, null); + mWiFiNetworkAgent.sendLinkProperties(lp); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, null); inOrder.verify(mMockNetd).clatdStop(iface); inOrder.verify(mMockDnsResolver).setPrefix64(netId, ""); inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId); @@ -7272,8 +7684,8 @@ public class ConnectivityServiceTest { // If the RA prefix appears while DNS discovery is in progress, discovery is stopped and // clatd is started with the prefix from the RA. lp.setNat64Prefix(pref64FromRa); - mCellNetworkAgent.sendLinkProperties(lp); - expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromRa); + mWiFiNetworkAgent.sendLinkProperties(lp); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromRa); inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString()); inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId); inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString()); @@ -7281,21 +7693,21 @@ public class ConnectivityServiceTest { // Withdraw the RA prefix so we can test the case where an RA prefix appears after DNS // discovery has succeeded. lp.setNat64Prefix(null); - mCellNetworkAgent.sendLinkProperties(lp); - expectNat64PrefixChange(callback, mCellNetworkAgent, null); + mWiFiNetworkAgent.sendLinkProperties(lp); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, null); inOrder.verify(mMockNetd).clatdStop(iface); inOrder.verify(mMockDnsResolver).setPrefix64(netId, ""); inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId); mService.mNetdEventCallback.onNat64PrefixEvent(netId, true /* added */, pref64FromDnsStr, 96); - expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromDns); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromDns); inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString()); // If an RA advertises the same prefix that was discovered by DNS, nothing happens: prefix // discovery is not stopped, and there are no callbacks. lp.setNat64Prefix(pref64FromDns); - mCellNetworkAgent.sendLinkProperties(lp); + mWiFiNetworkAgent.sendLinkProperties(lp); callback.assertNoCallback(); inOrder.verify(mMockNetd, never()).clatdStop(iface); inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString()); @@ -7305,7 +7717,7 @@ public class ConnectivityServiceTest { // If the RA is later withdrawn, nothing happens again. lp.setNat64Prefix(null); - mCellNetworkAgent.sendLinkProperties(lp); + mWiFiNetworkAgent.sendLinkProperties(lp); callback.assertNoCallback(); inOrder.verify(mMockNetd, never()).clatdStop(iface); inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString()); @@ -7315,8 +7727,8 @@ public class ConnectivityServiceTest { // If the RA prefix changes, clatd is restarted and prefix discovery is stopped. lp.setNat64Prefix(pref64FromRa); - mCellNetworkAgent.sendLinkProperties(lp); - expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromRa); + mWiFiNetworkAgent.sendLinkProperties(lp); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromRa); inOrder.verify(mMockNetd).clatdStop(iface); inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId); @@ -7330,8 +7742,8 @@ public class ConnectivityServiceTest { // If the RA prefix changes, clatd is restarted and prefix discovery is not started. lp.setNat64Prefix(newPref64FromRa); - mCellNetworkAgent.sendLinkProperties(lp); - expectNat64PrefixChange(callback, mCellNetworkAgent, newPref64FromRa); + mWiFiNetworkAgent.sendLinkProperties(lp); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, newPref64FromRa); inOrder.verify(mMockNetd).clatdStop(iface); inOrder.verify(mMockDnsResolver).setPrefix64(netId, ""); inOrder.verify(mMockNetd).clatdStart(iface, newPref64FromRa.toString()); @@ -7341,7 +7753,7 @@ public class ConnectivityServiceTest { // If the RA prefix changes to the same value, nothing happens. lp.setNat64Prefix(newPref64FromRa); - mCellNetworkAgent.sendLinkProperties(lp); + mWiFiNetworkAgent.sendLinkProperties(lp); callback.assertNoCallback(); assertEquals(newPref64FromRa, mCm.getLinkProperties(network).getNat64Prefix()); inOrder.verify(mMockNetd, never()).clatdStop(iface); @@ -7355,19 +7767,19 @@ public class ConnectivityServiceTest { // If the same prefix is learned first by DNS and then by RA, and clat is later stopped, // (e.g., because the network disconnects) setPrefix64(netid, "") is never called. lp.setNat64Prefix(null); - mCellNetworkAgent.sendLinkProperties(lp); - expectNat64PrefixChange(callback, mCellNetworkAgent, null); + mWiFiNetworkAgent.sendLinkProperties(lp); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, null); inOrder.verify(mMockNetd).clatdStop(iface); inOrder.verify(mMockDnsResolver).setPrefix64(netId, ""); inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId); mService.mNetdEventCallback.onNat64PrefixEvent(netId, true /* added */, pref64FromDnsStr, 96); - expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromDns); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromDns); inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString()); inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), any()); lp.setNat64Prefix(pref64FromDns); - mCellNetworkAgent.sendLinkProperties(lp); + mWiFiNetworkAgent.sendLinkProperties(lp); callback.assertNoCallback(); inOrder.verify(mMockNetd, never()).clatdStop(iface); inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString()); @@ -7378,10 +7790,10 @@ public class ConnectivityServiceTest { // When tearing down a network, clat state is only updated after CALLBACK_LOST is fired, but // before CONNECTIVITY_ACTION is sent. Wait for CONNECTIVITY_ACTION before verifying that // clat has been stopped, or the test will be flaky. - ConditionVariable cv = registerConnectivityBroadcast(1); - mCellNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - waitFor(cv); + ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED); + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + b.expectBroadcast(); inOrder.verify(mMockNetd).clatdStop(iface); inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId); @@ -7456,10 +7868,10 @@ public class ConnectivityServiceTest { .destroyNetworkCache(eq(mCellNetworkAgent.getNetwork().netId)); // Disconnect wifi - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED); reset(mNetworkManagementService); mWiFiNetworkAgent.disconnect(); - waitFor(cv); + b.expectBroadcast(); verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME)); // Clean up @@ -7581,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); @@ -7609,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); @@ -7625,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); @@ -7640,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); @@ -7692,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); @@ -7757,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())) @@ -7876,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); @@ -8080,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); @@ -8097,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); @@ -8112,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); @@ -8127,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", @@ -8163,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); @@ -8182,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); @@ -8405,6 +8825,7 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(genericRequest, genericNetworkCallback); mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); + waitForIdle(); final ConnectivityService.NetworkRequestInfo[] nriOutput = mService.requestsSortedById(); @@ -8450,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); @@ -8466,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/VcnTransportInfoTest.java b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java new file mode 100644 index 000000000000..31561901be9e --- /dev/null +++ b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.vcn; + +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; + +import android.net.wifi.WifiInfo; +import android.os.Parcel; + +import org.junit.Test; + +public class VcnTransportInfoTest { + private static final int SUB_ID = 1; + private static final int NETWORK_ID = 5; + private static final WifiInfo WIFI_INFO = + new WifiInfo.Builder().setNetworkId(NETWORK_ID).build(); + + private static final VcnTransportInfo CELL_UNDERLYING_INFO = new VcnTransportInfo(SUB_ID); + private static final VcnTransportInfo WIFI_UNDERLYING_INFO = new VcnTransportInfo(WIFI_INFO); + + @Test + public void testGetWifiInfo() { + assertEquals(WIFI_INFO, WIFI_UNDERLYING_INFO.getWifiInfo()); + + assertNull(CELL_UNDERLYING_INFO.getWifiInfo()); + } + + @Test + public void testGetSubId() { + assertEquals(SUB_ID, CELL_UNDERLYING_INFO.getSubId()); + + assertEquals(INVALID_SUBSCRIPTION_ID, WIFI_UNDERLYING_INFO.getSubId()); + } + + @Test + public void testEquals() { + assertEquals(CELL_UNDERLYING_INFO, CELL_UNDERLYING_INFO); + assertEquals(WIFI_UNDERLYING_INFO, WIFI_UNDERLYING_INFO); + assertNotEquals(CELL_UNDERLYING_INFO, WIFI_UNDERLYING_INFO); + } + + @Test + public void testParcelUnparcel() { + verifyParcelingIsNull(CELL_UNDERLYING_INFO); + verifyParcelingIsNull(WIFI_UNDERLYING_INFO); + } + + private void verifyParcelingIsNull(VcnTransportInfo vcnTransportInfo) { + Parcel parcel = Parcel.obtain(); + vcnTransportInfo.writeToParcel(parcel, 0 /* flags */); + assertNull(VcnTransportInfo.CREATOR.createFromParcel(parcel)); + } +} 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); + } +} |