Merge "Remove DisplayList.h dependency from Canvas.h"
diff --git a/.gitignore b/.gitignore
index c47cc8b..5018436 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 35f97ac..5c0dd63 100644
--- a/Android.bp
+++ b/Android.bp
@@ -532,6 +532,7 @@
"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 a060ad9..7e7feaf 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 9fa4b8e..2320b75 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -8,6 +8,7 @@
}
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 @@
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 @@
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 73a4a19..4a6194e 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 a5b7080..6fa8f85 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 @@
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 @@
/**
* 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 @@
/**
* 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 d4872e8..0427577 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 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 @@
*
* 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 @@
}
/**
- * 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 95f7d79..e4e030e 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 @@
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 @@
* 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 @@
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 4931cc0..62324b2 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 @@
/** @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 @@
}
/**
+ * 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 b754926..674f199 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.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.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 @@
try {
getResultProto =
mIcingSearchEngineLocked.get(
- createPrefix(packageName, databaseName) + namespace, uri);
+ createPrefix(packageName, databaseName) + namespace,
+ uri,
+ GetResultSpecProto.getDefaultInstance());
} finally {
mReadWriteLock.readLock().unlock();
}
@@ -691,8 +695,6 @@
* <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 @@
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 @@
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 @@
// 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 @@
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 ccd567d..f422ebc 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 @@
* @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 @@
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 @@
*
* @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 073a7f6..0d7d3e1 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.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 @@
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 @@
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 f8e5a8a..9be3049 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 6859747..39ca687 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 @@
@NonNull GlobalSearchSession session, @NonNull ExecutorService executor) {
mGlobalSearchSession = Preconditions.checkNotNull(session);
mExecutor = Preconditions.checkNotNull(executor);
+
}
@NonNull
@@ -72,4 +73,9 @@
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 e439c5a..3e81968 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 com.google.common.util.concurrent.ListenableFuture;
+import java.io.Closeable;
import java.util.Set;
/**
@@ -29,7 +30,7 @@
*
* <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 @@
@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 2d09247..cd867a4 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 @@
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 @@
*/
@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 18643ed..4441643 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 @@
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 0000000..b5d1838
--- /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 9989252..7aed32c 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 @@
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 @@
}
}
+ 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 @@
@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 @@
} 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 @@
// 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 @@
}
}
+ 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 @@
publishBinderService(Context.DEVICE_IDLE_CONTROLLER, mBinderService);
mLocalService = new LocalService();
publishLocalService(DeviceIdleInternal.class, mLocalService);
+ publishLocalService(PowerAllowlistInternal.class, new LocalPowerAllowlistService());
}
@Override
@@ -2667,7 +2721,9 @@
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 @@
* 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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 a9ca730..26b5abe 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 @@
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 a02f8de..736ee18 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.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.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 @@
*/
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 @@
*/
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 @@
* 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 @@
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 @@
clearAppStatsLocked(UserHandle.getUserId(uid), packageName);
mForegroundUids.delete(uid);
mUidToPackageCache.remove(uid);
+ mTempAllowlistCache.delete(uid);
+ mTempAllowlistGraceCache.delete(uid);
}
@Override
@@ -1988,10 +2010,15 @@
}
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 @@
}
}
+ 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 @@
// 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 @@
// 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 @@
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 @@
@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 @@
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 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 @@
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 @@
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 @@
return mEJRewardTopAppMs;
}
+ @VisibleForTesting
+ long getEJTempAllowlistGracePeriodMs() {
+ return mEJTempAllowlistGracePeriodMs;
+ }
@VisibleForTesting
@Nullable
@@ -3757,6 +3872,12 @@
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 ce3bcbe..103bb47 100644
--- a/apex/media/framework/api/current.txt
+++ b/apex/media/framework/api/current.txt
@@ -6,9 +6,11 @@
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 @@
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 36f6b94..25ccec2 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.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 @@
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 @@
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 String toString() {
String caps = new String(
"Supported Video MimeTypes: " + mSupportedVideoMimeTypes.toString());
+ caps += "Unsupported Video MimeTypes: " + mUnsupportedVideoMimeTypes.toString();
caps += "Supported HDR types: " + mSupportedHdrTypes.toString();
+ caps += "Unsupported HDR types: " + mUnsupportedHdrTypes.toString();
caps += "Supported slow motion: " + mIsSlowMotionSupported;
return caps;
}
@@ -159,9 +210,8 @@
};
/*
- * Returns a list that contains all the video codec mime types supported by the application.
- * The list will be empty if no codecs are supported by the application.
- * @return List of supported video codec mime types.
+ * Query the video codec mime types supported by the application.
+ * @return List of supported video codec mime types. The list will be empty if there are none.
*/
@NonNull
public List<String> getSupportedVideoMimeTypes() {
@@ -169,9 +219,17 @@
}
/*
- * Returns a list that contains all hdr types supported by the application.
- * The list will be empty if no hdr types are supported by the application.
- * @return List of supported hdr types.
+ * Query the video codec mime types that are not supported by the application.
+ * @return List of unsupported video codec mime types. The list will be empty if there are none.
+ */
+ @NonNull
+ public List<String> getUnsupportedVideoMimeTypes() {
+ return new ArrayList<>(mSupportedVideoMimeTypes);
+ }
+
+ /*
+ * Query all hdr types that are supported by the application.
+ * @return List of supported hdr types. The list will be empty if there are none.
*/
@NonNull
public List<String> getSupportedHdrTypes() {
@@ -179,6 +237,15 @@
}
/*
+ * Query all hdr types that are not supported by the application.
+ * @return List of unsupported hdr types. The list will be empty if there are none.
+ */
+ @NonNull
+ public List<String> getUnsupportedHdrTypes() {
+ return new ArrayList<>(mUnsupportedHdrTypes);
+ }
+
+ /*
* Whether handling of slow-motion video is supported
*/
public boolean isSlowMotionSupported() {
@@ -213,6 +280,12 @@
/** List of supported hdr types. */
private Set<String> mSupportedHdrTypes = new HashSet<>();
+ /** List of unsupported video codec mime types. */
+ private Set<String> mUnsupportedVideoMimeTypes = new HashSet<>();
+
+ /** List of unsupported hdr types. */
+ private Set<String> mUnsupportedHdrTypes = new HashSet<>();
+
private boolean mIsSlowMotionSupported = false;
/* Map to save the format read from the xml. */
@@ -299,26 +372,50 @@
case "HEVC":
if (isSupported) {
mSupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
+ } else {
+ mUnsupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
+ }
+ break;
+ case "VP9":
+ if (isSupported) {
+ mSupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_VP9);
+ } else {
+ mUnsupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_VP9);
+ }
+ break;
+ case "AV1":
+ if (isSupported) {
+ mSupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_AV1);
+ } else {
+ mUnsupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_AV1);
}
break;
case "HDR10":
if (isSupported) {
mSupportedHdrTypes.add(MediaFeature.HdrType.HDR10);
+ } else {
+ mUnsupportedHdrTypes.add(MediaFeature.HdrType.HDR10);
}
break;
case "HDR10Plus":
if (isSupported) {
mSupportedHdrTypes.add(MediaFeature.HdrType.HDR10_PLUS);
+ } else {
+ mUnsupportedHdrTypes.add(MediaFeature.HdrType.HDR10_PLUS);
}
break;
case "Dolby-Vision":
if (isSupported) {
mSupportedHdrTypes.add(MediaFeature.HdrType.DOLBY_VISION);
+ } else {
+ mUnsupportedHdrTypes.add(MediaFeature.HdrType.DOLBY_VISION);
}
break;
case "HLG":
if (isSupported) {
mSupportedHdrTypes.add(MediaFeature.HdrType.HLG);
+ } else {
+ mUnsupportedHdrTypes.add(MediaFeature.HdrType.HLG);
}
break;
case "SlowMotion":
@@ -348,8 +445,11 @@
@NonNull
public ApplicationMediaCapabilities build() {
Log.d(TAG,
- "Building ApplicationMediaCapabilities with: " + mSupportedHdrTypes.toString()
- + " " + mSupportedVideoMimeTypes.toString() + " "
+ "Building ApplicationMediaCapabilities with: (Supported HDR: "
+ + mSupportedHdrTypes.toString() + " Unsupported HDR: "
+ + mUnsupportedHdrTypes.toString() + ") (Supported Codec: "
+ + " " + mSupportedVideoMimeTypes.toString() + " Unsupported Codec:"
+ + mUnsupportedVideoMimeTypes.toString() + ") "
+ mIsSlowMotionSupported);
// If hdr is supported, application must also support hevc.
@@ -365,8 +465,7 @@
*
* @param codecMime Supported codec mime types. Must be one of the mime type defined
* in {@link MediaFormat}.
- * @throws UnsupportedOperationException if the codec mime type is not supported.
- * @throws IllegalArgumentException if mime type is not valid.
+ * @throws IllegalArgumentException if mime type is not valid.
*/
@NonNull
public Builder addSupportedVideoMimeType(
@@ -379,16 +478,49 @@
return new ArrayList<>(mSupportedVideoMimeTypes);
}
+ private boolean isValidVideoCodecMimeType(@NonNull String codecMime) {
+ if (!codecMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)
+ && !codecMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP9)
+ && !codecMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AV1)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Adds an unsupported video codec mime type.
+ *
+ * @param codecMime Unsupported codec mime type. Must be one of the mime type defined
+ * in {@link MediaFormat}.
+ * @throws IllegalArgumentException if mime type is not valid.
+ */
+ @NonNull
+ public Builder addUnsupportedVideoMimeType(
+ @NonNull String codecMime) {
+ if (!isValidVideoCodecMimeType(codecMime)) {
+ throw new IllegalArgumentException("Invalid codec mime type: " + codecMime);
+ }
+ mUnsupportedVideoMimeTypes.add(codecMime);
+ return this;
+ }
+
+ private List<String> getUnsupportedVideoMimeTypes() {
+ return new ArrayList<>(mUnsupportedVideoMimeTypes);
+ }
+
/**
* Adds a supported hdr type.
*
- * @param hdrType Supported hdr types. Must be one of the String defined in
+ * @param hdrType Supported hdr type. Must be one of the String defined in
* {@link MediaFeature.HdrType}.
* @throws IllegalArgumentException if hdrType is not valid.
*/
@NonNull
public Builder addSupportedHdrType(
@NonNull @MediaFeature.MediaHdrType String hdrType) {
+ if (!isValidVideoCodecHdrType(hdrType)) {
+ throw new IllegalArgumentException("Invalid hdr type: " + hdrType);
+ }
mSupportedHdrTypes.add(hdrType);
return this;
}
@@ -397,6 +529,37 @@
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 3d706e4..55c4629 100644
--- a/apex/media/framework/java/android/media/MediaTranscodeManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java
@@ -980,8 +980,15 @@
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 @@
"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 ef1e413..9088db8 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 @@
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 @@
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 @@
System.err.println("");
System.err.println(" sm supports-checkpoint");
System.err.println("");
+ System.err.println(" sm unmount-app-data-dirs PACKAGE_NAME PID USER_ID");
+ System.err.println("");
return 1;
}
}
diff --git a/core/api/current.txt b/core/api/current.txt
index bfa69dc..ba49b67 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -6760,7 +6760,7 @@
method public android.content.Intent getCropAndSetWallpaperIntent(android.net.Uri);
method public int getDesiredMinimumHeight();
method public int getDesiredMinimumWidth();
- method public android.graphics.drawable.Drawable getDrawable();
+ method @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public android.graphics.drawable.Drawable getDrawable();
method @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public android.graphics.drawable.Drawable getFastDrawable();
method public static android.app.WallpaperManager getInstance(android.content.Context);
method @Nullable public android.app.WallpaperColors getWallpaperColors(int);
@@ -8163,6 +8163,7 @@
method public long getAppBytes();
method public long getCacheBytes();
method public long getDataBytes();
+ method public long getExternalCacheBytes();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.StorageStats> CREATOR;
}
@@ -10350,6 +10351,7 @@
field public static final String BIOMETRIC_SERVICE = "biometric";
field public static final String BLOB_STORE_SERVICE = "blob_store";
field public static final String BLUETOOTH_SERVICE = "bluetooth";
+ field public static final String BUGREPORT_SERVICE = "bugreport";
field public static final String CAMERA_SERVICE = "camera";
field public static final String CAPTIONING_SERVICE = "captioning";
field public static final String CARRIER_CONFIG_SERVICE = "carrier_config";
@@ -10837,6 +10839,8 @@
field public static final String ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED";
field public static final String ACTION_POWER_USAGE_SUMMARY = "android.intent.action.POWER_USAGE_SUMMARY";
field public static final String ACTION_PROCESS_TEXT = "android.intent.action.PROCESS_TEXT";
+ field public static final String ACTION_PROFILE_ACCESSIBLE = "android.intent.action.PROFILE_ACCESSIBLE";
+ field public static final String ACTION_PROFILE_INACCESSIBLE = "android.intent.action.PROFILE_INACCESSIBLE";
field public static final String ACTION_PROVIDER_CHANGED = "android.intent.action.PROVIDER_CHANGED";
field public static final String ACTION_QUICK_CLOCK = "android.intent.action.QUICK_CLOCK";
field public static final String ACTION_QUICK_VIEW = "android.intent.action.QUICK_VIEW";
@@ -12049,7 +12053,6 @@
public static class PackageInstaller.Session implements java.io.Closeable {
method public void abandon();
- method @Deprecated public void addChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>) throws java.io.IOException;
method public void addChildSessionId(int);
method public void close();
method public void commit(@NonNull android.content.IntentSender);
@@ -12063,6 +12066,7 @@
method @NonNull public java.io.OutputStream openWrite(@NonNull String, long, long) throws java.io.IOException;
method public void removeChildSessionId(int);
method public void removeSplit(@NonNull String) throws java.io.IOException;
+ method @Deprecated public void setChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>, @Nullable byte[]) throws java.io.IOException;
method public void setStagingProgress(float);
method public void transfer(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
}
@@ -15543,7 +15547,6 @@
ctor public Point(int, int);
ctor public Point(@NonNull android.graphics.Point);
method public int describeContents();
- method public final void dump(@NonNull java.io.PrintWriter);
method public final boolean equals(int, int);
method public final void negate();
method public final void offset(int, int);
@@ -30082,6 +30085,24 @@
method public boolean unlinkToDeath(@NonNull android.os.IBinder.DeathRecipient, int);
}
+ public final class BugreportManager {
+ method public void cancelBugreport();
+ method public void startConnectivityBugreport(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback);
+ }
+
+ public abstract static class BugreportManager.BugreportCallback {
+ ctor public BugreportManager.BugreportCallback();
+ method public void onEarlyReportFinished();
+ method public void onError(int);
+ method public void onFinished();
+ method public void onProgress(@FloatRange(from=0.0f, to=100.0f) float);
+ field public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5; // 0x5
+ field public static final int BUGREPORT_ERROR_INVALID_INPUT = 1; // 0x1
+ field public static final int BUGREPORT_ERROR_RUNTIME = 2; // 0x2
+ field public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4; // 0x4
+ field public static final int BUGREPORT_ERROR_USER_DENIED_CONSENT = 3; // 0x3
+ }
+
public class Build {
ctor public Build();
method @NonNull public static java.util.List<android.os.Build.Partition> getFingerprintedPartitions();
@@ -34471,6 +34492,7 @@
field public static final String ACTION_LOCATION_SOURCE_SETTINGS = "android.settings.LOCATION_SOURCE_SETTINGS";
field public static final String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS";
field public static final String ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION = "android.settings.MANAGE_ALL_FILES_ACCESS_PERMISSION";
+ field public static final String ACTION_MANAGE_ALL_SUBSCRIPTIONS_SETTINGS = "android.settings.MANAGE_ALL_SUBSCRIPTIONS_SETTINGS";
field public static final String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS";
field public static final String ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION = "android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION";
field public static final String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS";
@@ -39764,6 +39786,7 @@
field public static final int BAND_25 = 25; // 0x19
field public static final int BAND_257 = 257; // 0x101
field public static final int BAND_258 = 258; // 0x102
+ field public static final int BAND_26 = 26; // 0x1a
field public static final int BAND_260 = 260; // 0x104
field public static final int BAND_261 = 261; // 0x105
field public static final int BAND_28 = 28; // 0x1c
@@ -39775,10 +39798,12 @@
field public static final int BAND_39 = 39; // 0x27
field public static final int BAND_40 = 40; // 0x28
field public static final int BAND_41 = 41; // 0x29
+ field public static final int BAND_46 = 46; // 0x2e
field public static final int BAND_48 = 48; // 0x30
field public static final int BAND_5 = 5; // 0x5
field public static final int BAND_50 = 50; // 0x32
field public static final int BAND_51 = 51; // 0x33
+ field public static final int BAND_53 = 53; // 0x35
field public static final int BAND_65 = 65; // 0x41
field public static final int BAND_66 = 66; // 0x42
field public static final int BAND_7 = 7; // 0x7
@@ -39804,6 +39829,7 @@
field public static final int BAND_93 = 93; // 0x5d
field public static final int BAND_94 = 94; // 0x5e
field public static final int BAND_95 = 95; // 0x5f
+ field public static final int BAND_96 = 96; // 0x60
}
public static final class AccessNetworkConstants.UtranBand {
@@ -41315,17 +41341,27 @@
public final class PhysicalChannelConfig implements android.os.Parcelable {
method public int describeContents();
- method public int getCellBandwidthDownlink();
- method public int getChannelNumber();
+ method @IntRange(from=1, to=261) public int getBand();
+ method @IntRange(from=1) public int getCellBandwidthDownlinkKhz();
+ method @IntRange(from=1) public int getCellBandwidthUplinkKhz();
+ method @Deprecated public int getChannelNumber();
method public int getConnectionStatus();
+ method @IntRange(from=0) public int getDownlinkChannelNumber();
+ method @IntRange(from=0) public int getDownlinkFrequencyKhz();
method public int getNetworkType();
method @IntRange(from=0, to=1007) public int getPhysicalCellId();
+ method @IntRange(from=0) public int getUplinkChannelNumber();
+ method @IntRange(from=0) public int getUplinkFrequencyKhz();
method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int BAND_UNKNOWN = 0; // 0x0
+ field public static final int CELL_BANDWIDTH_UNKNOWN = 0; // 0x0
field public static final int CHANNEL_NUMBER_UNKNOWN = -1; // 0xffffffff
field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1
field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2
field public static final int CONNECTION_UNKNOWN = -1; // 0xffffffff
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhysicalChannelConfig> CREATOR;
+ field public static final int FREQUENCY_UNKNOWN = -1; // 0xffffffff
+ field public static final int PHYSICAL_CELL_ID_MAXIMUM_VALUE = 1007; // 0x3ef
field public static final int PHYSICAL_CELL_ID_UNKNOWN = -1; // 0xffffffff
}
@@ -41408,6 +41444,49 @@
field public static final int INVALID = 2147483647; // 0x7fffffff
}
+ public final class SignalStrengthUpdateRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.Collection<android.telephony.SignalThresholdInfo> getSignalThresholdInfos();
+ method public boolean isReportingRequestedWhileIdle();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SignalStrengthUpdateRequest> CREATOR;
+ }
+
+ public static final class SignalStrengthUpdateRequest.Builder {
+ ctor public SignalStrengthUpdateRequest.Builder();
+ method @NonNull public android.telephony.SignalStrengthUpdateRequest build();
+ method @NonNull public android.telephony.SignalStrengthUpdateRequest.Builder setReportingRequestedWhileIdle(boolean);
+ method @NonNull public android.telephony.SignalStrengthUpdateRequest.Builder setSignalThresholdInfos(@NonNull java.util.Collection<android.telephony.SignalThresholdInfo>);
+ }
+
+ public final class SignalThresholdInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public static int getMaximumNumberOfThresholdsAllowed();
+ method public static int getMinimumNumberOfThresholdsAllowed();
+ method public int getRadioAccessNetworkType();
+ method public int getSignalMeasurementType();
+ method @NonNull public int[] getThresholds();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SignalThresholdInfo> CREATOR;
+ field public static final int SIGNAL_MEASUREMENT_TYPE_RSCP = 2; // 0x2
+ field public static final int SIGNAL_MEASUREMENT_TYPE_RSRP = 3; // 0x3
+ field public static final int SIGNAL_MEASUREMENT_TYPE_RSRQ = 4; // 0x4
+ field public static final int SIGNAL_MEASUREMENT_TYPE_RSSI = 1; // 0x1
+ field public static final int SIGNAL_MEASUREMENT_TYPE_RSSNR = 5; // 0x5
+ field public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRP = 6; // 0x6
+ field public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRQ = 7; // 0x7
+ field public static final int SIGNAL_MEASUREMENT_TYPE_SSSINR = 8; // 0x8
+ field public static final int SIGNAL_MEASUREMENT_TYPE_UNKNOWN = 0; // 0x0
+ }
+
+ public static final class SignalThresholdInfo.Builder {
+ ctor public SignalThresholdInfo.Builder();
+ method @NonNull public android.telephony.SignalThresholdInfo build();
+ method @NonNull public android.telephony.SignalThresholdInfo.Builder setRadioAccessNetworkType(int);
+ method @NonNull public android.telephony.SignalThresholdInfo.Builder setSignalMeasurementType(int);
+ method @NonNull public android.telephony.SignalThresholdInfo.Builder setThresholds(@NonNull int[]);
+ }
+
public final class SmsManager {
method public String createAppSpecificSmsToken(android.app.PendingIntent);
method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent);
@@ -49319,8 +49398,9 @@
method public void show(int);
field public static final int APPEARANCE_LIGHT_NAVIGATION_BARS = 16; // 0x10
field public static final int APPEARANCE_LIGHT_STATUS_BARS = 8; // 0x8
- field public static final int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1; // 0x1
- field public static final int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0; // 0x0
+ field public static final int BEHAVIOR_DEFAULT = 1; // 0x1
+ field @Deprecated public static final int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1; // 0x1
+ field @Deprecated public static final int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0; // 0x0
field public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2; // 0x2
}
@@ -51683,6 +51763,10 @@
}
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 2ebcb19..d91ea2c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -137,6 +137,7 @@
field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
field public static final String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
field public static final String MANAGE_TIME_AND_ZONE_DETECTION = "android.permission.MANAGE_TIME_AND_ZONE_DETECTION";
+ field public static final String MANAGE_UI_TRANSLATION = "android.permission.MANAGE_UI_TRANSLATION";
field public static final String MANAGE_USB = "android.permission.MANAGE_USB";
field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS";
field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE";
@@ -369,6 +370,8 @@
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
method public void setDeviceLocales(@NonNull android.os.LocaleList);
method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public static void setPersistentVrThread(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public boolean startProfile(@NonNull android.os.UserHandle);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public boolean stopProfile(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull android.os.UserHandle);
}
@@ -893,6 +896,8 @@
field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI";
field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL";
field public static final String EXTRA_PROVISIONING_ORGANIZATION_NAME = "android.app.extra.PROVISIONING_ORGANIZATION_NAME";
+ field public static final String EXTRA_PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE = "android.app.extra.PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE";
+ field public static final String EXTRA_PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER = "android.app.extra.PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER";
field public static final String EXTRA_PROVISIONING_SUPPORTED_MODES = "android.app.extra.PROVISIONING_SUPPORTED_MODES";
field public static final String EXTRA_PROVISIONING_SUPPORT_URL = "android.app.extra.PROVISIONING_SUPPORT_URL";
field public static final String EXTRA_PROVISIONING_TRIGGER = "android.app.extra.PROVISIONING_TRIGGER";
@@ -1922,7 +1927,6 @@
field public static final String BATTERY_STATS_SERVICE = "batterystats";
field public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000
field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000
- field public static final String BUGREPORT_SERVICE = "bugreport";
field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
field public static final String CONTEXTHUB_SERVICE = "contexthub";
field public static final String ETHERNET_SERVICE = "ethernet";
@@ -1944,6 +1948,7 @@
field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
field public static final String TETHERING_SERVICE = "tethering";
field public static final String TRANSLATION_MANAGER_SERVICE = "transformer";
+ field public static final String UI_TRANSLATION_SERVICE = "ui_translation";
field public static final String VR_SERVICE = "vrmanager";
field public static final String WIFI_NL80211_SERVICE = "wifinl80211";
field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager";
@@ -5350,9 +5355,9 @@
method public int disconnectFrontendToCiCam(int);
method public int getAvSyncHwId(@NonNull android.media.tv.tuner.filter.Filter);
method public long getAvSyncTime(int);
+ method @Nullable public java.util.List<android.media.tv.tuner.frontend.FrontendInfo> getAvailableFrontendInfos();
method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities();
method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
- method @Nullable public java.util.List<android.media.tv.tuner.frontend.FrontendInfo> getFrontendInfoList();
method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler();
method @Nullable public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener);
@@ -7852,24 +7857,10 @@
}
public final class BugreportManager {
- method @RequiresPermission(android.Manifest.permission.DUMP) public void cancelBugreport();
method @RequiresPermission(android.Manifest.permission.DUMP) public void requestBugreport(@NonNull android.os.BugreportParams, @Nullable CharSequence, @Nullable CharSequence);
method @RequiresPermission(android.Manifest.permission.DUMP) public void startBugreport(@NonNull android.os.ParcelFileDescriptor, @Nullable android.os.ParcelFileDescriptor, @NonNull android.os.BugreportParams, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback);
}
- public abstract static class BugreportManager.BugreportCallback {
- ctor public BugreportManager.BugreportCallback();
- method public void onEarlyReportFinished();
- method public void onError(int);
- method public void onFinished();
- method public void onProgress(@FloatRange(from=0.0f, to=100.0f) float);
- field public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5; // 0x5
- field public static final int BUGREPORT_ERROR_INVALID_INPUT = 1; // 0x1
- field public static final int BUGREPORT_ERROR_RUNTIME = 2; // 0x2
- field public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4; // 0x4
- field public static final int BUGREPORT_ERROR_USER_DENIED_CONSENT = 3; // 0x3
- }
-
public final class BugreportParams {
ctor public BugreportParams(int);
method public int getMode();
@@ -10242,6 +10233,7 @@
method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
method @Nullable public final String getTelecomCallId();
method @Deprecated public void onAudioStateChanged(android.telecom.AudioState);
+ method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean);
method public final void resetConnectionTime();
method public void setCallDirection(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long);
@@ -10418,6 +10410,7 @@
}
public final class RemoteConnection {
+ method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean);
method @Deprecated public void setAudioState(android.telecom.AudioState);
}
@@ -10601,6 +10594,7 @@
public static final class CarrierConfigManager.Wifi {
field public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT = "wifi.hotspot_maximum_client_count";
field public static final String KEY_PREFIX = "wifi.";
+ field public static final String KEY_SUGGESTION_SSID_LIST_WITH_MAC_RANDOMIZATION_DISABLED = "wifi.suggestion_ssid_list_with_mac_randomization_disabled";
}
public final class CarrierRestrictionRules implements android.os.Parcelable {
@@ -13445,6 +13439,17 @@
}
+package android.view.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 015ae64..9e83136 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -254,6 +254,7 @@
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 @@
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 @@
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.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 @@
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.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 @@
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 c6f42f7..a31cfae 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 8c62e9c..520959c 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3883,6 +3883,52 @@
}
/**
+ * 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 f7f42a6..986051c 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -98,7 +98,7 @@
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 @@
/**
* Update information about which app IDs are on the temp whitelist.
+ * @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 changingAppId,
- boolean adding);
+ 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 f541e1a..d0d5df9 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -54,6 +54,7 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
}
/** @hide */
+ public IRemoteTransition getRemoteTransition() {
+ return mRemoteTransition;
+ }
+
+ /** @hide */
public static ActivityOptions fromBundle(Bundle bOptions) {
return bOptions != null ? new ActivityOptions(bOptions) : null;
}
@@ -1724,6 +1750,9 @@
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 a23dd35..161b731 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1645,7 +1645,7 @@
*/
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 c1ed7b2..bac5025 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1995,8 +1995,7 @@
}
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 9fdff59..f7097fa 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -470,9 +470,6 @@
|| 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 1b8f049..0019fd1 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -686,4 +686,22 @@
* {@link android.content.pm.PackageManager#getHoldLockToken()}.
*/
void holdLock(in IBinder token, in int durationMs);
+
+ /**
+ * Starts a profile.
+ * @param userId the user id of the profile.
+ * @return true if the profile has been successfully started or if the profile is already
+ * running, false if profile failed to start.
+ * @throws IllegalArgumentException if the user is not a profile.
+ */
+ boolean startProfile(int userId);
+
+ /**
+ * Stops a profile.
+ * @param userId the user id of the profile.
+ * @return true if the profile has been successfully stopped or is already stopped. Otherwise
+ * the exceptions listed below are thrown.
+ * @throws IllegalArgumentException if the user is not a profile.
+ */
+ boolean stopProfile(int userId);
}
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index b1a8f9b..323af821 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -32,6 +32,7 @@
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 @@
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 @@
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 @@
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 @@
mConversationId = in.readString();
mDemoted = in.readBoolean();
mImportantConvo = in.readBoolean();
+ mDeletedTime = in.readLong();
}
@Override
@@ -341,6 +346,7 @@
dest.writeString(mConversationId);
dest.writeBoolean(mDemoted);
dest.writeBoolean(mImportantConvo);
+ dest.writeLong(mDeletedTime);
}
/**
@@ -378,6 +384,14 @@
* @hide
*/
@TestApi
+ public void setDeletedTimeMs(long time) {
+ mDeletedTime = time;
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
public void setImportantConversation(boolean importantConvo) {
mImportantConvo = importantConvo;
}
@@ -766,6 +780,13 @@
/**
* @hide
*/
+ public long getDeletedTimeMs() {
+ return mDeletedTime;
+ }
+
+ /**
+ * @hide
+ */
@SystemApi
public int getUserLockedFields() {
return mUserLockedFields;
@@ -906,6 +927,8 @@
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 @@
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 @@
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 @@
&& 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 @@
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 @@
+ ", 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 dd016a2..bbda871 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -31,7 +31,6 @@
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.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 @@
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 @@
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 ab0901d..ac2f223 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -63,6 +63,7 @@
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 @@
* 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 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 @@
* 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 @@
*/
@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 @@
*/
@UnsupportedAppUsage
public @Nullable WallpaperColors getWallpaperColors(int which, int userId) {
+ assertUiContext("getWallpaperColors");
return sGlobals.getWallpaperColors(which, userId, mContext.getDisplayId());
}
@@ -1261,6 +1268,7 @@
@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 @@
* @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 @@
* @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 @@
* @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 @@
*/
@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 @@
*/
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
public void clear() throws IOException {
+ assertUiContext("clear");
setStream(openDefaultWallpaper(mContext, FLAG_SYSTEM), null, false);
}
@@ -2094,6 +2107,10 @@
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 71af5e3..54e1ac43 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2536,6 +2536,22 @@
"android.app.extra.PROVISIONING_SUPPORTED_MODES";
/**
+ * A boolean extra which determines whether to skip the ownership disclaimer screen during the
+ * provisioning flow. The default value is {@code false}.
+ *
+ * If the value is {@code true}, it is the responsibility of the provisioning initiator to
+ * show the relevant disclaimer.
+ *
+ * <p>This extra is only respected when provided alongside the {@link
+ * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent action.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER =
+ "android.app.extra.PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER";
+
+ /**
* An {@link ArrayList} of {@link Integer} extra specifying the allowed provisioning modes.
* <p>This extra will be passed to the admin app's {@link #ACTION_GET_PROVISIONING_MODE}
* activity, whose result intent must contain {@link #EXTRA_PROVISIONING_MODE} set to one of
@@ -2564,6 +2580,30 @@
public static final int PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE = 3;
/**
+ * A {@code boolean} flag that indicates whether the provisioning flow should return before
+ * starting the admin app's {@link #ACTION_ADMIN_POLICY_COMPLIANCE} handler. The default value
+ * is {@code true}.
+ *
+ * <p>If this extra is set to {@code true}, then when the provisioning flow returns back to the
+ * provisioning initiator, provisioning will not be complete. The provisioning initiator can
+ * use this opportunity to do its own preparatory steps prior to the launch of the admin app's
+ * {@link #ACTION_ADMIN_POLICY_COMPLIANCE} handler. It is the responsibility of the
+ * provisioning initiator to ensure that the provisioning flow is then resumed and completed.
+ *
+ * <p>If this extra is set to {@code false}, then when the provisioning flow returns back to
+ * the provisioning initiator, provisioning will be complete. Note that device owner
+ * provisioning is not currently supported for the this scenario.
+ *
+ * <p>This extra is only respected when provided alongside the {@link
+ * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent action.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE =
+ "android.app.extra.PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE";
+
+ /**
* Activity action: Starts the administrator to show policy compliance for the provisioning.
* This action is used any time that the administrator has an opportunity to show policy
* compliance before the end of setup wizard. This could happen as part of the admin-integrated
@@ -2690,6 +2730,60 @@
/** @hide */
@TestApi
public static final int OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES = 22;
+ /** @hide */
+ @TestApi
+ public static final int OPERATION_CLEAR_APPLICATION_USER_DATA = 23;
+ /** @hide */
+ @TestApi
+ public static final int OPERATION_INSTALL_CA_CERT = 24;
+ /** @hide */
+ @TestApi
+ public static final int OPERATION_INSTALL_KEY_PAIR = 25;
+ /** @hide */
+ @TestApi
+ public static final int OPERATION_INSTALL_SYSTEM_UPDATE = 26;
+ /** @hide */
+ @TestApi
+ public static final int OPERATION_REMOVE_ACTIVE_ADMIN = 27;
+ /** @hide */
+ @TestApi
+ public static final int OPERATION_REMOVE_KEY_PAIR = 28;
+ /** @hide */
+ @TestApi
+ public static final int OPERATION_REQUEST_BUGREPORT = 29;
+ /** @hide */
+ @TestApi
+ public static final int OPERATION_SET_ALWAYS_ON_VPN_PACKAGE = 30;
+ /** @hide */
+ @TestApi
+ public static final int OPERATION_SET_CAMERA_DISABLED = 31;
+ /** @hide */
+ @TestApi
+ public static final int OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY = 32;
+ /** @hide */
+ @TestApi
+ public static final int OPERATION_SET_GLOBAL_PRIVATE_DNS = 33;
+ /** @hide */
+ @TestApi
+ public static final int OPERATION_SET_LOGOUT_ENABLED = 34;
+ /** @hide */
+ @TestApi
+ public static final int OPERATION_SET_MASTER_VOLUME_MUTED = 35;
+ /** @hide */
+ @TestApi
+ public static final int OPERATION_SET_OVERRIDE_APNS_ENABLED = 36;
+ /** @hide */
+ @TestApi
+ public static final int OPERATION_SET_PERMISSION_GRANT_STATE = 37;
+ /** @hide */
+ @TestApi
+ public static final int OPERATION_SET_PERMISSION_POLICY = 38;
+ /** @hide */
+ @TestApi
+ public static final int OPERATION_SET_RESTRICTIONS_PROVIDER = 39;
+ /** @hide */
+ @TestApi
+ public static final int OPERATION_UNINSTALL_CA_CERT = 40;
private static final String PREFIX_OPERATION = "OPERATION_";
@@ -2716,7 +2810,25 @@
OPERATION_SET_LOCK_TASK_PACKAGES,
OPERATION_SET_PACKAGES_SUSPENDED,
OPERATION_SET_TRUST_AGENT_CONFIGURATION,
- OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES
+ OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES,
+ OPERATION_CLEAR_APPLICATION_USER_DATA,
+ OPERATION_INSTALL_CA_CERT,
+ OPERATION_INSTALL_KEY_PAIR,
+ OPERATION_INSTALL_SYSTEM_UPDATE,
+ OPERATION_REMOVE_ACTIVE_ADMIN,
+ OPERATION_REMOVE_KEY_PAIR,
+ OPERATION_REQUEST_BUGREPORT,
+ OPERATION_SET_ALWAYS_ON_VPN_PACKAGE,
+ OPERATION_SET_CAMERA_DISABLED,
+ OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY,
+ OPERATION_SET_GLOBAL_PRIVATE_DNS,
+ OPERATION_SET_LOGOUT_ENABLED,
+ OPERATION_SET_MASTER_VOLUME_MUTED,
+ OPERATION_SET_OVERRIDE_APNS_ENABLED,
+ OPERATION_SET_PERMISSION_GRANT_STATE,
+ OPERATION_SET_PERMISSION_POLICY,
+ OPERATION_SET_RESTRICTIONS_PROVIDER,
+ OPERATION_UNINSTALL_CA_CERT
})
@Retention(RetentionPolicy.SOURCE)
public static @interface DevicePolicyOperation {
@@ -9823,7 +9935,7 @@
* Designates a specific service component as the provider for making permission requests of a
* local or remote administrator of the user.
* <p/>
- * Only a profile owner can designate the restrictions provider.
+ * Only a device owner or profile owner can designate the restrictions provider.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param provider The component name of the service that implements
diff --git a/core/java/android/app/role/RoleControllerManager.java b/core/java/android/app/role/RoleControllerManager.java
index 8dde2c5..ba1f612 100644
--- a/core/java/android/app/role/RoleControllerManager.java
+++ b/core/java/android/app/role/RoleControllerManager.java
@@ -20,8 +20,6 @@
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 @@
*
* @hide
*/
-@SystemService(Context.ROLE_CONTROLLER_SERVICE)
-@TestApi
public class RoleControllerManager {
private static final String LOG_TAG = RoleControllerManager.class.getSimpleName();
@@ -199,32 +195,11 @@
}
/**
- * @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 @@
* @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 8b2e07b..0fcf44d 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -174,6 +174,9 @@
@NonNull
private final Object mListenersLock = new Object();
+ @NonNull
+ private final RoleControllerManager mRoleControllerManager;
+
/**
* @hide
*/
@@ -181,6 +184,7 @@
mContext = context;
mService = IRoleManager.Stub.asInterface(ServiceManager.getServiceOrThrow(
Context.ROLE_SERVICE));
+ mRoleControllerManager = new RoleControllerManager(context);
}
/**
@@ -676,6 +680,44 @@
}
}
+ /**
+ * Check whether a role should be visible to user.
+ *
+ * @param roleName name of the role to check for
+ * @param executor the executor to execute callback on
+ * @param callback the callback to receive whether the role should be visible to user
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+ @TestApi
+ public void isRoleVisible(@NonNull String roleName,
+ @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+ mRoleControllerManager.isRoleVisible(roleName, executor, callback);
+ }
+
+ /**
+ * Check whether an application is visible for a role.
+ *
+ * While an application can be qualified for a role, it can still stay hidden from user (thus
+ * not visible). If an application is visible for a role, we may show things related to the role
+ * for it, e.g. showing an entry pointing to the role settings in its application info page.
+ *
+ * @param roleName the name of the role to check for
+ * @param packageName the package name of the application to check for
+ * @param executor the executor to execute callback on
+ * @param callback the callback to receive whether the application is visible for the role
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+ @TestApi
+ public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName,
+ @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+ mRoleControllerManager.isApplicationVisibleForRole(roleName, packageName, executor,
+ callback);
+ }
+
private static class OnRoleHoldersChangedListenerDelegate
extends IOnRoleHoldersChangedListener.Stub {
diff --git a/core/java/android/app/search/OWNERS b/core/java/android/app/search/OWNERS
new file mode 100644
index 0000000..92835c2
--- /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 158f9f3..a76154a 100644
--- a/core/java/android/app/search/SearchAction.java
+++ b/core/java/android/app/search/SearchAction.java
@@ -70,20 +70,20 @@
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 @@
@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 @@
@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 @@
@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 @@
*/
@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 4757557..8d25d7b 100644
--- a/core/java/android/app/usage/StorageStats.java
+++ b/core/java/android/app/usage/StorageStats.java
@@ -32,6 +32,7 @@
/** {@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 @@
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 @@
this.codeBytes = in.readLong();
this.dataBytes = in.readLong();
this.cacheBytes = in.readLong();
+ this.externalCacheBytes = in.readLong();
}
@Override
@@ -98,6 +111,7 @@
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 5ccceca..2190140 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4520,6 +4520,15 @@
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 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 @@
* 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 13a1381..7843d97 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3660,7 +3660,7 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
"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 0000000..045c55f
--- /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 0000000..14b8df8
--- /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 9a73be9..f72288c 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -35,7 +35,7 @@
void write(String name, long offsetBytes, long lengthBytes, in ParcelFileDescriptor fd);
void stageViaHardLink(String target);
- void addChecksums(String name, in Checksum[] checksums);
+ void setChecksums(String name, in Checksum[] checksums, in byte[] signature);
void removeSplit(String splitName);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 01d4c280..248be0f 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1247,12 +1247,15 @@
}
/**
- * 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 @@
* 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 17c4d25..03d4d5e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3513,6 +3513,17 @@
public static final String FEATURE_TUNER = "android.hardware.tv.tuner";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports a enabling/disabling sensor privacy for
+ * camera. When sensory privacy for the camera is enabled no camera data is send to clients,
+ * e.g. the view finder in a camera app would appear blank.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
* the necessary changes to support app enumeration.
*
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index da75fba..522f4ca 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -2191,7 +2191,7 @@
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 056af77..f8fd4a5 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -295,8 +295,10 @@
pi.applicationInfo.publicSourceDir = apexFile.getPath();
if (apexInfo.isFactory) {
pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
} else {
pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
+ pi.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
if (apexInfo.isActive) {
pi.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index f7c4c2c..ec6c233 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -881,7 +881,11 @@
}
}
- static int getMaxLengthValuesArray(Sensor sensor, int sdkLevel) {
+ /**
+ * Return sensor's maximum length of values array
+ * @hide
+ */
+ public static int getMaxLengthValuesArray(Sensor sensor, int sdkLevel) {
// RotationVector length has changed to 3 to 5 for API level 18
// Set it to 3 for backward compatibility.
if (sensor.mType == Sensor.TYPE_ROTATION_VECTOR
diff --git a/core/java/android/hardware/input/InputDeviceSensorManager.java b/core/java/android/hardware/input/InputDeviceSensorManager.java
index 56c2cdd..89db857 100644
--- a/core/java/android/hardware/input/InputDeviceSensorManager.java
+++ b/core/java/android/hardware/input/InputDeviceSensorManager.java
@@ -25,6 +25,7 @@
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.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 @@
@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 @@
@Override
public void onInputDeviceChanged(int deviceId) {
synchronized (mInputSensorLock) {
- if (mSensors.containsKey(deviceId)) {
- mSensors.remove(deviceId);
- }
+ mSensors.remove(deviceId);
updateInputDeviceSensorInfoLocked(deviceId);
}
}
@@ -196,16 +196,21 @@
+ " 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 @@
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 @@
// 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 @@
}
}
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 @@
}
/**
+ * 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 @@
}
/**
- * Create SensorEvent object for input device, with specified device ID, sensor Type,
- * sensor event timestamp, accuracy, and sensor values.
- */
- private SensorEvent createSensorEvent(InputDevice inputDevice, int sensorType, int accuracy,
- long timestamp, float[] values) {
- synchronized (mInputSensorLock) {
- Sensor sensor = getInputDeviceSensorLocked(inputDevice.getId(), sensorType);
- if (sensor == null) {
- Slog.wtf(TAG, "Can't get sensor type " + sensorType + " for input device "
- + inputDevice);
- }
- SensorEvent event = new SensorEvent(sensor, accuracy, timestamp, values);
- if (event == null) {
- Slog.wtf(TAG, "Failed to create SensorEvent.");
- }
- return event;
- }
- }
-
- /**
* Return the default sensor object for input device, for specific sensor type.
*/
private Sensor getSensorForInputDevice(int deviceId, int type) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 930950e..899af5a 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -72,7 +72,6 @@
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 @@
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 @@
} 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 @@
} 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 @@
} 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 c029dea..cad0db2 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -160,7 +160,7 @@
/** @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 58ea915..bb91f89 100644
--- a/core/java/android/net/metrics/IpConnectivityLog.java
+++ b/core/java/android/net/metrics/IpConnectivityLog.java
@@ -137,7 +137,7 @@
* @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 04b585c..80ac64b 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 @@
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
similarity index 64%
rename from media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
rename to core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl
index edf96dd..f8ae492 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 039360a..d531cdb 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 @@
*
* @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 @@
*
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 b881a339..2ccdc26 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -25,7 +25,12 @@
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 @@
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 @@
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/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl
similarity index 64%
copy from media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
copy to core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl
index edf96dd..6cb6ee6 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.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,7 @@
* 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 */
+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 0000000..dd7c86d8
--- /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 33736d3..305c686 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -22,6 +22,7 @@
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.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 @@
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 @@
/**
* 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 @@
* 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 @@
* 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 @@
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 @@
}
/**
+ * 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 @@
* 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 @@
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 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 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 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 @@
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 7ec7fff..869a727 100644
--- a/core/java/android/os/CombinedVibrationEffect.java
+++ b/core/java/android/os/CombinedVibrationEffect.java
@@ -87,8 +87,14 @@
}
/** @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 @@
return mEffect;
}
+ @Override
+ public long getDuration() {
+ return mEffect.getDuration();
+ }
+
/** @hide */
@Override
public void validate() {
@@ -272,12 +283,17 @@
}
@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 @@
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 @@
}
@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 @@
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 @@
"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 @@
}
@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 0326b72..a46af97 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -1442,11 +1442,13 @@
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 @@
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 b951aca..814a248 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -612,12 +612,14 @@
* @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 086180e..b13be9f 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -2194,6 +2194,33 @@
onVmPolicyViolation(new IncorrectContextUseViolation(message, originStack));
}
+ /**
+ * A helper method to verify if the {@code context} is a UI context and throw
+ * {@link IncorrectContextUseViolation} if the {@code context} is not a UI context.
+ *
+ * @param context The context to verify if it is a UI context
+ * @param methodName The asserted method name
+ *
+ * @see Context#isUiContext()
+ * @see IncorrectContextUseViolation
+ *
+ * @hide
+ */
+ public static void assertUiContext(@NonNull Context context, @NonNull String methodName) {
+ if (vmIncorrectContextUseEnabled() && !context.isUiContext()) {
+ final String errorMessage = "Tried to access UI related API" + methodName
+ + " from a non-UI Context:" + context;
+ final String message = "UI-related services, such as WindowManager, WallpaperService "
+ + "or LayoutInflater should be accessed from Activity or other UI "
+ + "Contexts. Use an Activity or a Context created with "
+ + "Context#createWindowContext(int, Bundle), which are adjusted to "
+ + "the configuration and visual bounds of an area on screen.";
+ final Exception exception = new IllegalAccessException(errorMessage);
+ StrictMode.onIncorrectContextUsed(message, exception);
+ Log.e(TAG, errorMessage + " " + message, exception);
+ }
+ }
+
/** Assume locked until we hear otherwise */
private static volatile boolean sUserKeyUnlocked = false;
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index c0b2ada..df3beb2 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -317,7 +317,7 @@
*/
@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 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);
- }
-
- /** @hide */
public Prebaked(int effectId, boolean fallback, int effectStrength) {
mEffectId = effectId;
mFallback = fallback;
mEffectStrength = effectStrength;
+ mFallbackEffect = null;
+ }
+
+ /** @hide */
+ public Prebaked(int effectId, int effectStrength, @NonNull VibrationEffect fallbackEffect) {
+ mEffectId = effectId;
+ mFallback = true;
+ mEffectStrength = effectStrength;
+ mFallbackEffect = fallbackEffect;
}
public int getId() {
@@ -829,26 +837,27 @@
/** @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.
- return this;
- }
-
- /**
- * Set the effect strength of the prebaked effect.
- */
- public void setEffectStrength(int strength) {
- if (!isValidEffectStrength(strength)) {
- throw new IllegalArgumentException("Invalid effect strength: " + strength);
+ if (mFallbackEffect != null) {
+ VibrationEffect scaledFallback = mFallbackEffect.scale(scaleFactor);
+ if (!mFallbackEffect.equals(scaledFallback)) {
+ return new Prebaked(mEffectId, mEffectStrength, scaledFallback);
+ }
}
- mEffectStrength = strength;
+ // Prebaked effect strength cannot be scaled with this method.
+ return this;
}
/**
@@ -858,6 +867,16 @@
return mEffectStrength;
}
+ /**
+ * Return the fallback effect, if set.
+ *
+ * @hide
+ */
+ @Nullable
+ public VibrationEffect getFallbackEffect() {
+ return mFallbackEffect;
+ }
+
private static boolean isValidEffectStrength(int strength) {
switch (strength) {
case EffectStrength.LIGHT:
@@ -901,15 +920,13 @@
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 @@
return "Prebaked{mEffectId=" + mEffectId
+ ", mEffectStrength=" + mEffectStrength
+ ", mFallback=" + mFallback
+ + ", mFallbackEffect=" + mFallbackEffect
+ "}";
}
@@ -927,6 +945,7 @@
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 @@
// 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 @@
/** @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 59292baa..9fdc72b 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -36,6 +36,7 @@
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 @@
@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 @@
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 @@
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 67317c7..d32928c 100644
--- a/core/java/android/os/storage/DiskInfo.java
+++ b/core/java/android/os/storage/DiskInfo.java
@@ -50,6 +50,8 @@
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 @@
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 99bdfd1..4669b20 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -195,4 +195,5 @@
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 e599776..c28b59b 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -183,8 +183,9 @@
}
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 @@
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 @@
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 @@
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 @@
* 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 @@
* 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 @@
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 @@
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 @@
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 @@
}
}
- 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;
}
- for (int usageNum = 0; usageNum < rawUsages.size(); usageNum++) {
- PackageAttribution usageAttr = rawUsages.get(usageNum).toPackageAttr();
- // If this attribution has a special attribution, do not remove it
- if (specialAttributions.contains(usageAttr)) {
+ List<PackageAttribution> toRemoveProxies = new ArrayList<>();
+ for (List<PackageAttribution> proxyList: proxies) {
+ PackageAttribution lastLink = proxyList.get(proxyList.size() - 1);
+ if (!specialAttributions.contains(lastLink)) {
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;
+ for (int proxyNum = 0; proxyNum < proxyList.size(); proxyNum++) {
+ if (!proxyList.get(proxyNum).equals(lastLink)) {
+ toRemoveProxies.add(proxyList.get(proxyNum));
}
}
}
- for (int i = 0; i < toRemove.size(); i++) {
- rawUsages.remove(toRemove.get(i));
+ for (int usageNum = 0; usageNum < rawUsages.size(); usageNum++) {
+ OpUsage usage = rawUsages.get(usageNum);
+
+ // If this attribution has a special attribution, do not remove it
+ 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 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;
+ }
+ }
+ }
+
+ 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 9b570b7..27ba72b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1018,6 +1018,20 @@
"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 0000000..92835c2
--- /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 1878d61..8492363 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -19,171 +19,268 @@
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 the name associated by the system to this font.
+ * Returns a file to the font file.
+ *
+ * @return a font file.
*/
- public @NonNull String getFontName() {
- return mFontName;
+ 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;
+ }
+
+ /**
+ * Return a font variation settings.
+ *
+ * @return a font variation settings.
+ */
+ 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,26 +309,81 @@
/**
* 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.
*/
+ 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 a locale list if associated.
+ *
+ * @return non-null if a locale list is associated. Otherwise null.
+ */
+ public @NonNull LocaleList getLocaleList() {
+ return mLocaleList;
+ }
+
+ /**
+ * Returns a text height variant.
+ *
+ * @return text height variant.
+ */
+ public @Variant int getTextHeightVariant() {
+ return mVariant;
+ }
+
+ /**
+ * 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;
@@ -239,25 +391,23 @@
/**
* 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;
+ return mFonts.toArray(new Font[0]);
}
/**
- * Returns the comma separated BCP47 complient languages for this family. May be null.
+ * Returns the comma separated BCP47 compliant languages for this family. May be null.
+ * @deprecated Use getLocaleList instead
+ * @hide
*/
+ @Deprecated
public @NonNull String getLanguages() {
- return mLanguages;
- }
-
- /**
- * Returns the font variant for this family, e.g. "elegant" or "compact". May be null.
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public @Variant int getVariant() {
- return mVariant;
+ return mLocaleList.toLanguageTags();
}
}
}
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index 17d3ae4..471f2c2 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -24,11 +24,12 @@
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 @@
*/
@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 2d26c64..b4e1172 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -642,7 +642,7 @@
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 @@
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 @@
@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 5a64a5d..a334907 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_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 @@
@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 bf377b0..d68e903 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -106,7 +106,9 @@
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 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 @@
}
/**
+ * 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 c018d1c..c61baf6 100644
--- a/core/java/android/view/PendingInsetsController.java
+++ b/core/java/android/view/PendingInsetsController.java
@@ -100,6 +100,9 @@
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 ac5d14e..258a72c 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -270,7 +270,7 @@
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 @@
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 26e3bb2..7ac57b5 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -232,7 +232,7 @@
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 25967b3..b8840ba 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3790,7 +3790,7 @@
* <p>Since this flag is a modifier for {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, it only
* has an effect when used in combination with that flag.</p>
*
- * @deprecated Use {@link WindowInsetsController#BEHAVIOR_SHOW_BARS_BY_SWIPE} instead.
+ * @deprecated Use {@link WindowInsetsController#BEHAVIOR_DEFAULT} instead.
*/
@Deprecated
public static final int SYSTEM_UI_FLAG_IMMERSIVE = 0x00000800;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7e0ebbc..426edbc 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -26,7 +26,6 @@
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.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
-import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE;
-import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH;
+import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
@@ -2167,10 +2165,8 @@
if ((sysUiVis & SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0
|| (flags & FLAG_FULLSCREEN) != 0) {
inOutParams.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
- } else if ((sysUiVis & SYSTEM_UI_FLAG_IMMERSIVE) != 0) {
- inOutParams.insetsFlags.behavior = BEHAVIOR_SHOW_BARS_BY_SWIPE;
} else {
- inOutParams.insetsFlags.behavior = BEHAVIOR_SHOW_BARS_BY_TOUCH;
+ inOutParams.insetsFlags.behavior = BEHAVIOR_DEFAULT;
}
}
@@ -9154,14 +9150,14 @@
* Handles an inbound request for scroll capture from the system. If a client is not already
* active, a search will be dispatched through the view tree to locate scrolling content.
* <p>
- * Either {@link IScrollCaptureCallbacks#onClientConnected(IScrollCaptureConnection, Rect,
+ * Either {@link IScrollCaptureCallbacks#onConnected(IScrollCaptureConnection, Rect,
* Point)} or {@link IScrollCaptureCallbacks#onUnavailable()} will be returned
* depending on the results of the search.
*
* @param callbacks to receive responses
* @see ScrollCaptureTargetResolver
*/
- private void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) {
+ public void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) {
LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>();
// Window (root) level callbacks
@@ -9169,10 +9165,12 @@
// Search through View-tree
View rootView = getView();
- Point point = new Point();
- Rect rect = new Rect(0, 0, rootView.getWidth(), rootView.getHeight());
- getChildVisibleRect(rootView, rect, point);
- rootView.dispatchScrollCaptureSearch(rect, point, targetList);
+ if (rootView != null) {
+ Point point = new Point();
+ Rect rect = new Rect(0, 0, rootView.getWidth(), rootView.getHeight());
+ getChildVisibleRect(rootView, rect, point);
+ rootView.dispatchScrollCaptureSearch(rect, point, targetList);
+ }
// No-op path. Scroll capture not offered for this window.
if (targetList.isEmpty()) {
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index e879bb4..fb9bcbd 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -21,6 +21,7 @@
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 @@
}
/**
- * 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 @@
* @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 4f7e841..45fa41b 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -663,24 +663,34 @@
}
/**
- * Message for taking fullscreen screenshot
+ * Invoke screenshot flow to capture a full-screen image.
* @hide
*/
int TAKE_SCREENSHOT_FULLSCREEN = 1;
/**
- * Message for taking screenshot of selected region.
+ * Invoke screenshot flow allowing the user to select a region.
* @hide
*/
int TAKE_SCREENSHOT_SELECTED_REGION = 2;
/**
- * Message for handling a screenshot flow with an image provided by the caller.
+ * Invoke screenshot flow with an image provided by the caller.
* @hide
*/
int TAKE_SCREENSHOT_PROVIDED_IMAGE = 3;
/**
+ * Enum listing the types of screenshot requests available.
+ *
+ * @hide
+ */
+ @IntDef({TAKE_SCREENSHOT_FULLSCREEN,
+ TAKE_SCREENSHOT_SELECTED_REGION,
+ TAKE_SCREENSHOT_PROVIDED_IMAGE})
+ @interface ScreenshotType {}
+
+ /**
* Enum listing the possible sources from which a screenshot was originated. Used for logging.
*
* @hide
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index 8c35520..dd55f04 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -89,6 +89,15 @@
*/
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 93b5a2e..b1d3967 100644
--- a/core/java/android/view/accessibility/OWNERS
+++ b/core/java/android/view/accessibility/OWNERS
@@ -9,3 +9,4 @@
ogunwale@google.com
jjaggi@google.com
pweaver@google.com
+ryanlwlin@google.com
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index 5b32649..c1913f6 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -137,6 +137,15 @@
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 @@
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 d34c8d5..578ed8c 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 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 @@
}
/**
- * @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 @@
}
/**
- * @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 @@
}
/**
- * @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 73addf4..e175453 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 @@
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 0000000..eeb463a
--- /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 b9ff26b..6281ee9 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -39,6 +39,7 @@
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 @@
/**
* 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 e4de400..ed20d26 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -196,11 +196,6 @@
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 @@
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 0000000..e0ddf05
--- /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 55d47cb..af37fbc 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 @@
* 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/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl b/core/java/android/window/TransitionFilter.aidl
similarity index 64%
copy from media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
copy to core/java/android/window/TransitionFilter.aidl
index edf96dd..19c76d1 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
+++ b/core/java/android/window/TransitionFilter.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,6 @@
* limitations under the License.
*/
-package android.media.tv.tunerresourcemanager;
+package android.window;
-/**
- * Simple container of the FrontendInfo struct defined in the TunerHAL 1.0 interface.
- *
- * @hide
- */
-parcelable TunerFrontendInfo {
- int handle;
-
- int frontendType;
-
- int exclusiveGroupId;
-}
+parcelable TransitionFilter;
diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java
new file mode 100644
index 0000000..4421f06
--- /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/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl b/core/java/android/window/TransitionRequestInfo.aidl
similarity index 64%
copy from media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
copy to core/java/android/window/TransitionRequestInfo.aidl
index edf96dd..d2b9ccf 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
+++ b/core/java/android/window/TransitionRequestInfo.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,6 @@
* limitations under the License.
*/
-package android.media.tv.tunerresourcemanager;
+package android.window;
-/**
- * Simple container of the FrontendInfo struct defined in the TunerHAL 1.0 interface.
- *
- * @hide
- */
-parcelable TunerFrontendInfo {
- int handle;
-
- int frontendType;
-
- int exclusiveGroupId;
-}
+parcelable TransitionRequestInfo;
diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java
new file mode 100644
index 0000000..cc493ab
--- /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 c235c82..6cfd498 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -182,7 +182,7 @@
* 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 c0cc483..47d8334 100644
--- a/core/java/com/android/internal/app/ChooserActivityLogger.java
+++ b/core/java/com/android/internal/app/ChooserActivityLogger.java
@@ -120,7 +120,7 @@
@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 c0648ab..2e7629a 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -129,7 +129,7 @@
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 33aa190..f105320 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -19,6 +19,8 @@
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.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 @@
* @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 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 @@
// 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 @@
}
/**
- * 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 @@
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 @@
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 @@
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 @@
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 790d7f7..6860759e 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -69,15 +69,16 @@
// 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 4512fba..adebde0 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -2019,6 +2019,7 @@
if (getForeground() != null) {
drawableChanged();
}
+ notifyCaptionHeightChanged();
}
}
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index b644df4..b744a5d 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -130,12 +130,16 @@
* 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 @@
* 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 @@
}
/**
- * 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 @@
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 @@
* 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 b9c2fd5..200e0dd 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -41,7 +41,6 @@
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 @@
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 @@
* 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 9095f05..8fb2f9c 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 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 @@
mDisabledFlags2 = disabledFlags2;
mImeToken = imeToken;
mNavbarColorManagedByIme = navbarColorManagedByIme;
+ mBehavior = behavior;
mAppFullscreen = appFullscreen;
- mAppImmersive = appImmersive;
mTransientBarTypes = transientBarTypes;
}
@@ -79,8 +79,8 @@
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 @@
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 59c0c00..d67bd7a 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.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 @@
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 @@
// We don't care about pid, pass in -1
return mContext.checkPermission(permissionType, -1, uid);
}
+
+ /**
+ * Returns true if the |uid| holds NETWORK_SETTINGS permission.
+ */
+ public boolean checkNetworkSettingsPermission(int uid) {
+ return getUidPermission(android.Manifest.permission.NETWORK_SETTINGS, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * Returns true if the |uid| holds NETWORK_SETUP_WIZARD permission.
+ */
+ public boolean checkNetworkSetupWizardPermission(int uid) {
+ return getUidPermission(android.Manifest.permission.NETWORK_SETUP_WIZARD, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * Returns true if the |uid| holds NETWORK_STACK permission.
+ */
+ public boolean checkNetworkStackPermission(int uid) {
+ return getUidPermission(android.Manifest.permission.NETWORK_STACK, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * Returns true if the |uid| holds MAINLINE_NETWORK_STACK permission.
+ */
+ public boolean checkMainlineNetworkStackPermission(int uid) {
+ return getUidPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
}
diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp
index 0fb2911..a9db91b 100644
--- a/core/jni/android_os_HwBlob.cpp
+++ b/core/jni/android_os_HwBlob.cpp
@@ -257,7 +257,17 @@
// 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 2ff474b..4dc8121 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1784,7 +1784,7 @@
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 fa046c6..d3c2d31 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -626,6 +626,7 @@
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 51fb264..253aece 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -617,6 +617,9 @@
<protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_UNAVAILABLE" />
<protected-broadcast android:name="com.android.server.pm.DISABLE_QUIET_MODE_AFTER_UNLOCK" />
+ <protected-broadcast android:name="android.intent.action.PROFILE_ACCESSIBLE" />
+ <protected-broadcast android:name="android.intent.action.PROFILE_INACCESSIBLE" />
+
<protected-broadcast android:name="com.android.server.retaildemo.ACTION_RESET_DEMO" />
<protected-broadcast android:name="android.intent.action.DEVICE_LOCKED_CHANGED" />
@@ -2676,11 +2679,11 @@
The app can check whether it has this authorization by calling
{@link android.provider.Settings#canDrawOverlays
Settings.canDrawOverlays()}.
- <p>Protection level: signature|appop|installer|pre23|development|recents -->
+ <p>Protection level: signature|appop|preinstalled|pre23|development -->
<permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
android:label="@string/permlab_systemAlertWindow"
android:description="@string/permdesc_systemAlertWindow"
- android:protectionLevel="signature|appop|installer|pre23|development|recents" />
+ android:protectionLevel="signature|appop|preinstalled|pre23|development" />
<!-- @SystemApi @hide Allows an application to create windows using the type
{@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY},
@@ -3105,7 +3108,7 @@
<!-- @SystemApi Allows an application to read system update info.
@hide -->
<permission android:name="android.permission.READ_SYSTEM_UPDATE_INFO"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows the system to bind to an application's task services
@hide -->
@@ -3160,6 +3163,11 @@
<permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application to set, update and remove the credential management app.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP"
+ android:protectionLevel="signature" />
+
<!-- ========================================= -->
<!-- Permissions for special development tools -->
<!-- ========================================= -->
@@ -3612,6 +3620,15 @@
<permission android:name="android.permission.BIND_TRANSLATION_SERVICE"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows apps to use ui translation functions.
+ <p>Protection level: signature|privileged
+ <p> TODO(b/177703453): Restrict to a specific Role instead of allowing access to any
+ privileged app.
+ @hide Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.MANAGE_UI_TRANSLATION"
+ android:protectionLevel="signature|privileged" />
+
<!-- Must be required by a android.service.contentsuggestions.ContentSuggestionsService,
to ensure that only the system can bind to it.
@SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 4828f3f..bd017fd 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 9d87ddc..4955e47 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 2a6d379..770c304 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 aab5906..2e23ee4 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 bcc9fe2..475fa7d 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 7531077..6378045 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 93e0802..80c071c 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 08cab8d..6dd7b6a6 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 f3ad8da..ff9b003 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 d080d05..d2a4f7d 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 f7c27bc..47be220 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 1639718..e7e4cc9 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 7fb2042..47416cf 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 2e443e8..54e9dfd 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 e056b98..b759be6 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 88519df..7c7c178 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 1baa7f9..0cbbb01 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 687afae..e923d65 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 db34264..6e17242 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 7d1d04d..759fe01 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 528f7b9..64bc3d1 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 9c0ab18..08555e8 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 8de5734..4818589 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 5a944f3..39fc77c 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 be27d70..935d679 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 dc2af5a..09f289a 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 c4bf3f7..6bedfca 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 998845f..d8edc5d 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 8548506..76ab6ab 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 4f27856..a591d31 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 f374b96..a094117 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 981fff4..aebab8c 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 09ee61a..efdf842 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 0539da1..29803b8 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 969145f..510621b 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 7c7c4eb..802feeeb 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 1703493..2c01776 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 8a73d70..2bdabc8 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 263c87e..cf493c0 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 7b616b4..9de08bc 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 96cf902..f38be01 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 8db1191..2a70449 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 8bd2f49..9fc9f36 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 d812de9..fd81606b 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 acd46ce..612193c 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 ceee938..760d920 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 f260ebe..da18b18 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 5fe3733..4fdd570f 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 b7536b4..b18ce23 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 5d18f82..d21ab5a 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 4b1b1b8..778a15b 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 a5577c8..dc5175b 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 afd49ee..a14ea62 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 657a1d3..f768d17 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 8eed7f5..c6831a8 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 b813d9e..ff0b00e 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 9ef66df..4ada7a2 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 a2c3759..54e064d 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 3ceb626..804b77d 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 6849333..8d669c4 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 84e5a73..9837b88 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 507d759..ef7ee98 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 84e5a73..9837b88 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 4b1d3ac..68dad1c 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 1588c2b..03383f9 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 db0d5ad..cfa9bf1 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 546a64b..f8dfc26 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 a15676d..4a5c5da 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 cc694fa..5ee9031 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 5cadfd5..a430d56 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 bba8700..186a78a 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 3525ceb..cf9496d 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 61eac81..f3e5f91 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 666e980..2687aa1 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 5c6b7df..01ffd62 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 d27757c..40968eb 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 6c5ea97..342ff20 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 6ae2e8e..cf2c6f1 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 ae7f801..8f54c27 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 9bfb68f..a8bc4ba 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 e11291c..0af241e 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 6940140..4503305 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 1955b31..92b007c 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 b95974d..df2d5ea 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 58607f0..3f2d547 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -57,7 +57,8 @@
<string name="imei" msgid="2157082351232630390">"IMEI"</string>
<string name="meid" msgid="3291227361605924674">"MEID"</string>
<string name="ClipMmi" msgid="4110549342447630629">"I-ID Yocingo Olungenayo"</string>
- <string name="ClirMmi" msgid="4702929460236547156">"I-ID Yomshayeli Ephumayo"</string>
+ <!-- no translation found for ClirMmi (6752346475055446417) -->
+ <skip />
<string name="ColpMmi" msgid="4736462893284419302">"I-ID yomugqa exhumekile"</string>
<string name="ColrMmi" msgid="5889782479745764278">"I-ID yomugqa oxhumekile ikhawulelwe"</string>
<string name="CfMmi" msgid="8390012691099787178">"Ukudlulisa ikholi"</string>
@@ -148,8 +149,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"I-Wi-Fi kuphela"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g>Ukwenza ikholi kwe-Cross Sim"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Akudlulisiwe"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> emuva kwamasekhondi angu-<xliff:g id="TIME_DELAY">{2}</xliff:g>"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 0185714..bb26655 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 1845faa..272e130 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -58,6 +58,7 @@
<item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
+ <item><xliff:g id="id">@string/status_bar_no_calling</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_sensors_off</xliff:g></item>
</string-array>
@@ -94,6 +95,7 @@
<string translatable="false" name="status_bar_microphone">microphone</string>
<string translatable="false" name="status_bar_camera">camera</string>
<string translatable="false" name="status_bar_airplane">airplane</string>
+ <string translatable="false" name="status_bar_no_calling">no_calling</string>
<string translatable="false" name="status_bar_sensors_off">sensors_off</string>
<string translatable="false" name="status_bar_screen_record">screen_record</string>
@@ -991,6 +993,11 @@
<!-- Time to wait while a button is pressed before triggering a very long press. -->
<integer name="config_veryLongPressTimeout">3500</integer>
+ <!-- Time to wait before sending a HOME intent when waking up from power/home button.
+ (0 - do not send HOME intent on wakeup)
+ -->
+ <integer name="config_wakeUpToLastStateTimeoutMillis">0</integer>
+
<!-- Package name for default keyguard appwidget [DO NOT TRANSLATE] -->
<string name="widget_default_package_name" translatable="false"></string>
@@ -4610,4 +4617,9 @@
If omitted, image editing will not be offered via Chooser.
This name is in the ComponentName flattened format (package/class) [DO NOT TRANSLATE] -->
<string name="config_systemImageEditor" translatable="false"></string>
+
+ <!-- Whether to force WindowOrientationListener to keep listening to its sensor, even when
+ dreaming. This allows the AoD to rotate on devices without a wake device_orientation
+ sensor. Note that this flag should only be enabled for development/testing use. -->
+ <bool name="config_forceOrientationListenerEnabledWhileDreaming">false</bool>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c72a0cd..0c86905 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -438,6 +438,7 @@
<java-symbol type="integer" name="config_veryLongPressTimeout" />
<java-symbol type="integer" name="config_longPressOnBackBehavior" />
<java-symbol type="bool" name="config_allowStartActivityForLongPressOnPowerInSetup" />
+ <java-symbol type="integer" name="config_wakeUpToLastStateTimeoutMillis" />
<java-symbol type="integer" name="config_lowMemoryKillerMinFreeKbytesAdjust" />
<java-symbol type="integer" name="config_lowMemoryKillerMinFreeKbytesAbsolute" />
<java-symbol type="integer" name="config_max_pan_devices" />
@@ -2947,6 +2948,7 @@
<java-symbol type="string" name="status_bar_secure" />
<java-symbol type="string" name="status_bar_clock" />
<java-symbol type="string" name="status_bar_airplane" />
+ <java-symbol type="string" name="status_bar_no_calling" />
<java-symbol type="string" name="status_bar_mobile" />
<java-symbol type="string" name="status_bar_ethernet" />
<java-symbol type="string" name="status_bar_vpn" />
@@ -4146,4 +4148,6 @@
<java-symbol type="bool" name="config_attachNavBarToAppDuringTransition" />
<java-symbol type="bool" name="config_enableBackSound" />
+
+ <java-symbol type="bool" name="config_forceOrientationListenerEnabledWhileDreaming" />
</resources>
diff --git a/core/tests/coretests/src/android/content/OWNERS b/core/tests/coretests/src/android/content/OWNERS
index 696aa11..912db1e 100644
--- a/core/tests/coretests/src/android/content/OWNERS
+++ b/core/tests/coretests/src/android/content/OWNERS
@@ -1,4 +1,3 @@
per-file ContextTest.java = file:/services/core/java/com/android/server/wm/OWNERS
per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
-per-file AppSearchPersonTest.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS
per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS
diff --git a/core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java b/core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java
new file mode 100644
index 0000000..1ff88f7
--- /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 0000000..da92e69
--- /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 0000000..711f5f0
--- /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 05ff218..65ea2a8 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.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.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 @@
}
}
- 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 @@
@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();
+ 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());
- final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(SYSTEM_FONTS_XML,
- SYSTEM_FONT_DIR, oemCustomization, fallbackMap);
-
- 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 @@
+ "</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 @@
+ "</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 @@
+ "</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 @@
+ "</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 @@
+ "</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 @@
+ "</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 @@
+ "</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 @@
+ "</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 @@
+ "</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 @@
+ "</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 @@
+ "</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 @@
+ "</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 @@
+ "</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 @@
+ "</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 @@
+ "</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 @@
+ "</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 392c6b7..d12f495 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.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.runner.RunWith;
import java.nio.ByteOrder;
-import java.util.HashMap;
import java.util.Map;
import java.util.Random;
@@ -197,10 +195,10 @@
@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 c357414..1d56e17 100644
--- a/core/tests/coretests/src/android/os/VibrationEffectTest.java
+++ b/core/tests/coretests/src/android/os/VibrationEffectTest.java
@@ -39,6 +39,7 @@
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 @@
}
@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 @@
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 64e6f82..e301037 100644
--- a/core/tests/coretests/src/android/text/FontFallbackSetup.java
+++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java
@@ -19,14 +19,15 @@
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.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 @@
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 af13cc0..2770ed8 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.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 @@
}
@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 f371a7f..c67174f 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_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 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.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 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 @@
}
/**
+ * Ensure scroll capture request handles a ViewRootImpl with no view tree.
+ */
+ @Test
+ public void requestScrollCapture_withoutContentRoot() {
+ final CountDownLatch latch = new CountDownLatch(1);
+ mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() {
+ @Override
+ public void onUnavailable() {
+ latch.countDown();
+ }
+ });
+ try {
+ if (latch.await(100, TimeUnit.MILLISECONDS)) {
+ return; // pass
+ }
+ } catch (InterruptedException e) { /* ignore */ }
+ fail("requestScrollCapture did not respond");
+ }
+
+ /**
* When window doesn't have focus, keys should be dropped.
*/
@Test
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
index 82788c8..5def552 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 @@
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 5371a0f..ccd873d 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.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 @@
@Presubmit
public class AbsSeekBarTest {
+ public static final int PADDING = 10;
private Context mContext;
private AbsSeekBar mBar;
@@ -59,34 +59,42 @@
@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 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 @@
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 2ede751..c9c81ac 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 org.junit.Test;
+import java.util.Arrays;
+
/**
* Test class for {@link MeasuredEnergyStats}.
*
@@ -114,7 +116,7 @@
}
@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 @@
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 @@
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 @@
}
@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 7eca320..272f228 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 @@
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 @@
assertThat(copy.mDisabledFlags2).isEqualTo(original.mDisabledFlags2);
assertThat(copy.mImeToken).isSameInstanceAs(original.mImeToken);
assertThat(copy.mNavbarColorManagedByIme).isEqualTo(original.mNavbarColorManagedByIme);
+ assertThat(copy.mBehavior).isEqualTo(original.mBehavior);
assertThat(copy.mAppFullscreen).isEqualTo(original.mAppFullscreen);
- assertThat(copy.mAppImmersive).isEqualTo(original.mAppImmersive);
assertThat(copy.mTransientBarTypes).isEqualTo(original.mTransientBarTypes);
}
diff --git a/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java b/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java
index 4c3eaeb..7175f56 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 @@
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 @@
mFineLocationPermission = PackageManager.PERMISSION_DENIED;
mAllowCoarseLocationApps = AppOpsManager.MODE_ERRORED;
mAllowFineLocationApps = AppOpsManager.MODE_ERRORED;
+ mNetworkSettingsPermission = PackageManager.PERMISSION_DENIED;
}
private void setupMockInterface() {
@@ -151,6 +155,8 @@
.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 @@
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 fb8b17c..201f649 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -102,6 +102,14 @@
}
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 32666c8..6132d53 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 0000000..4fd91c3
--- /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 73fff72..ff38117 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.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 @@
/* 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 @@
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 @@
}
}
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 @@
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 61a4749..cf2f970 100644
--- a/graphics/java/android/graphics/Point.java
+++ b/graphics/java/android/graphics/Point.java
@@ -20,8 +20,6 @@
import android.os.Parcel;
import android.os.Parcelable;
-import java.io.PrintWriter;
-
/**
* Point holds two integer coordinates
*/
@@ -72,17 +70,6 @@
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 2d626b9..8dd7f31 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -220,7 +220,7 @@
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 7f2e503..1ace322 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 @@
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 @@
* 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));
- }
-
- /**
- * Creates a new RuntimeShader.
- *
- * @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.
- */
- public RuntimeShader(@NonNull String sksl, @Nullable byte[] uniforms,
- @Nullable Shader[] shaderInputs, boolean isOpaque) {
- this(sksl, uniforms, shaderInputs, isOpaque, ColorSpace.get(ColorSpace.Named.SRGB));
- }
-
- private RuntimeShader(@NonNull String sksl, @Nullable byte[] uniforms,
- @Nullable Shader[] shaderInputs, boolean isOpaque,
- ColorSpace colorSpace) {
- super(colorSpace);
- mUniforms = uniforms;
- mInputShaders = shaderInputs;
+ public RuntimeShader(@NonNull String sksl, boolean isOpaque) {
+ super(ColorSpace.get(ColorSpace.Named.SRGB));
mIsOpaque = isOpaque;
- mNativeInstanceRuntimeShaderFactory = nativeCreateShaderFactory(sksl);
- NoImagePreloadHolder.sRegistry.registerNativeAllocation(this,
- mNativeInstanceRuntimeShaderFactory);
+ mNativeInstanceRuntimeShaderBuilder = nativeCreateBuilder(sksl);
+ NoImagePreloadHolder.sRegistry.registerNativeAllocation(
+ this, mNativeInstanceRuntimeShaderBuilder);
}
/**
- * 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 float 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 value
*/
- public void updateUniforms(@Nullable byte[] uniforms) {
- mUniforms = uniforms;
+ public void setUniform(@NonNull String uniformName, float value) {
+ setUniform(uniformName, new float[] {value});
+ }
+
+ /**
+ * 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 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 uniformName name matching the uniform declared in the SKSL shader
+ * @param values
+ */
+ 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 48b474d..f1866cd 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -45,7 +45,6 @@
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 @@
// 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 @@
/** @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 @@
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 @@
/** @hide */
public static void loadPreinstalledSystemFontMap() {
- final HashMap<String, Typeface> systemFontMap = new HashMap<>();
- Pair<FontConfig.Alias[], Map<String, FontFamily[]>> pair =
- SystemFonts.initializePreinstalledFonts();
- initSystemDefaultTypefaces(systemFontMap, pair.second, pair.first);
- setSystemFontMap(systemFontMap);
+ final FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig();
+ final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig);
+ final Map<String, Typeface> typefaceMap =
+ SystemFonts.buildSystemTypefaces(fontConfig, fallback);
+ setSystemFontMap(typefaceMap);
}
static {
diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
index f95da82..1ad6fbe 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 @@
* 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 @@
*
* 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 75ea120..2f0c26f 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -18,6 +18,7 @@
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 @@
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 @@
}
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 16a53c2..54167b4 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -23,11 +23,9 @@
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.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 @@
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 @@
}
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 @@
@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 @@
}
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 @@
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 @@
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 @@
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 @@
}
/**
- * @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
+ */
+ public static @NonNull FontConfig getSystemPreinstalledFontConfig() {
+ return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, null);
+ }
+
+ /* 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());
+ }
+ }
+
+ /**
+ * Build the system fallback from 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);
+ 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 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);
+ 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;
}
-
- for (int i = 0; i < oemCustomization.mAdditionalNamedFamilies.size(); ++i) {
- appendNamedFamily(oemCustomization.mAdditionalNamedFamilies.get(i),
- bufferCache, fallbackListMap);
- }
-
- // 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 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);
- }
-
- 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);
+ appendNamedFamily(xmlFamily, bufferCache, fallbackListMap);
}
- }
- 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();
+ // 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);
+ }
}
+
+ // 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 0000000..14d6626
--- /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 0000000..1fde2b5
--- /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 2f444b3..97819c5 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -17,10 +17,13 @@
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.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 @@
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 @@
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 c70c986..4a67135 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -996,6 +996,7 @@
*/
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 6c733ba..33e8ded 100644
--- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationChunkedStreamer.java
+++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationChunkedStreamer.java
@@ -139,7 +139,9 @@
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 0290d9f..2cfb13e 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 0146b72..7aedc1b 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 @@
@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 faa4a0e..c9b38d0 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 @@
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 8d0e965..dd43139 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 @@
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 @@
@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 94b2cc0..d066cf9 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.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 @@
}
@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 821a007..e958648 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 @@
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 48d6a7b..eaa704f 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.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.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 @@
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 @@
}
};
+ 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 @@
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 @@
OneHandedTutorialHandler tutorialHandler,
OneHandedGestureHandler gestureHandler,
OneHandedTimeoutHandler timeoutHandler,
+ OneHandedUiEventLogger uiEventsLogger,
IOverlayManager overlayManager,
TaskStackListenerImpl taskStackListener,
ShellExecutor mainExecutor,
@@ -176,6 +193,8 @@
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 @@
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 @@
* 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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
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 @@
}
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
similarity index 80%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEvents.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedUiEventLogger.java
index 79ddd2b..38ffb07 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 @@
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 @@
"one_handed_settings_timeout_seconds_12"
};
+ public OneHandedUiEventLogger(UiEventLogger uiEventLogger) {
+ mUiEventLogger = uiEventLogger;
+ }
+
/**
* Events definition that related to One-Handed gestures.
*/
@@ -112,6 +112,7 @@
mId = id;
}
+ @Override
public int getId() {
return mId;
}
@@ -159,6 +160,7 @@
mId = id;
}
+ @Override
public int getId() {
return mId;
}
@@ -169,12 +171,8 @@
* 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 @@
* @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 de3bb29..f8b4dd9 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 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 4cd2c50..f1e06f7 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.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 @@
@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 0000000..cf141c6a
--- /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 3b2ac70..c085168 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 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.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 @@
/** 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
} 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 @@
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 @@
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 @@
* 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 @@
/**
* 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 @@
}
@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 85bf4a1..05e4539 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 @@
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 @@
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 9586fd1..bdc429c 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 @@
* 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 e9d3eb7..26fabbd 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 @@
* 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 @@
}
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 b5a36f5..e2439f2 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 @@
* 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 @@
@Test
fun testNonResizableLaunchInLegacySplitScreenTest() {
- val testTag = "NonResizableLaunchInLegacySplitScreenTest"
+ val testTag = "testNonResizableLaunchInLegacySplitScreenTest"
runWithFlicker(transitionSetup) {
withTestName { testTag }
@@ -64,7 +66,8 @@
}
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 90577ef..d004c06 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.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 @@
* 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 @@
@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 @@
@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 391cb2a..8ee9263 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 @@
*
* 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 923f2a4..594b4c1 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 @@
* 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 4578f68..6ee0491 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 @@
* 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 2b94c5f..8c90124 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 @@
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 16d13f4..f141167 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.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 @@
@Mock
OneHandedTimeoutHandler mMockTimeoutHandler;
@Mock
+ OneHandedUiEventLogger mMockUiEventLogger;
+ @Mock
IOverlayManager mMockOverlayManager;
@Mock
TaskStackListenerImpl mMockTaskStackListener;
@@ -89,6 +90,7 @@
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 c451b8b2..c3e6bf3 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 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 @@
ShellExecutor mMockShellMainExecutor;
@Mock
Handler mMockShellMainHandler;
+ @Mock
+ OneHandedUiEventLogger mMockUiEventLogger;
+
@Before
public void setUp() {
@@ -75,6 +77,7 @@
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
similarity index 67%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedEventsTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedUiEventLoggerTest.java
index 492c34e..e29fc6a 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 @@
@RunWith(Parameterized.class)
@SmallTest
-public class OneHandedEventsTest extends OneHandedTestCase {
+public class OneHandedUiEventLoggerTest extends OneHandedTestCase {
private UiEventLoggerFake mUiEventLogger;
@@ -48,7 +48,6 @@
@Before
public void setFakeLoggers() {
mUiEventLogger = new UiEventLoggerFake();
- OneHandedEvents.sUiEventLogger = mUiEventLogger;
}
@Test
@@ -63,42 +62,42 @@
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 c46e59a..f3bee4b 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 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 @@
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 @@
@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 @@
.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 @@
// 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 @@
// 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 @@
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 @@
@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 @@
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 6bf2e99..11a9086 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 @@
// 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 815ffde..1ebc489 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -819,10 +819,10 @@
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 b9370e9..155f6df 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -151,7 +151,7 @@
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 ea9fea97..fa0c45b 100644
--- a/libs/hwui/canvas/CanvasOps.h
+++ b/libs/hwui/canvas/CanvasOps.h
@@ -161,7 +161,7 @@
}
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 19ef7e3..fdfa288 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -30,7 +30,7 @@
class SkAnimatedImage;
class SkCanvasState;
-class SkRuntimeEffect;
+class SkRuntimeShaderBuilder;
class SkVertices;
namespace minikin {
@@ -138,7 +138,7 @@
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 764bc4c..f055c6e 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -76,6 +76,11 @@
|| (!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 1b309bc..cbfffd5 100644
--- a/libs/hwui/hwui/ImageDecoder.h
+++ b/libs/hwui/hwui/ImageDecoder.h
@@ -38,6 +38,7 @@
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 96e912f..ad7741b 100644
--- a/libs/hwui/jni/ImageDecoder.cpp
+++ b/libs/hwui/jni/ImageDecoder.cpp
@@ -465,7 +465,7 @@
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 aaec60b..1dc5cd9 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 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 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 360492d..855d56e 100644
--- a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
+++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
@@ -144,7 +144,8 @@
static void android_view_DisplayListCanvas_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 @@
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 3142d92..7859145 100644
--- a/libs/hwui/pipeline/skia/AnimatedDrawables.h
+++ b/libs/hwui/pipeline/skia/AnimatedDrawables.h
@@ -61,13 +61,13 @@
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 @@
}
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 e6c6e10..3498f71 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -102,12 +102,12 @@
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 0b4bb75..ee7c4d8 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -94,9 +94,9 @@
uirenderer::CanvasPropertyPrimitive* radius,
uirenderer::CanvasPropertyPaint* paint,
uirenderer::CanvasPropertyPrimitive* progress,
- sk_sp<SkRuntimeEffect> runtimeEffect) {
+ const SkRuntimeShaderBuilder& effectBuilder) {
drawDrawable(mDisplayList->allocateDrawable<AnimatedRipple>(x, y, radius, paint, progress,
- runtimeEffect));
+ effectBuilder));
}
void SkiaRecordingCanvas::enableZ(bool enableZ) {
@@ -308,10 +308,13 @@
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 0b543ae..8d7a21a 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -72,7 +72,7 @@
uirenderer::CanvasPropertyPrimitive* radius,
uirenderer::CanvasPropertyPaint* paint,
uirenderer::CanvasPropertyPrimitive* progress,
- sk_sp<SkRuntimeEffect> runtimeEffect) override;
+ const SkRuntimeShaderBuilder& effectBuilder) override;
virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
diff --git a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl
index a1b13ba..e55678d9 100644
--- a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl
+++ b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl
@@ -19,6 +19,8 @@
import android.media.metrics.NetworkEvent;
import android.media.metrics.PlaybackErrorEvent;
import android.media.metrics.PlaybackMetrics;
+import android.media.metrics.PlaybackStateEvent;
+import android.media.metrics.TrackChangeEvent;
/**
* Interface to the playback manager service.
@@ -27,6 +29,8 @@
interface IPlaybackMetricsManager {
void reportPlaybackMetrics(in String sessionId, in PlaybackMetrics metrics, int userId);
String getSessionId(int userId);
- void reportPlaybackErrorEvent(in String sessionId, in PlaybackErrorEvent event, int userId);
void reportNetworkEvent(in String sessionId, in NetworkEvent event, int userId);
+ void reportPlaybackErrorEvent(in String sessionId, in PlaybackErrorEvent event, int userId);
+ void reportPlaybackStateEvent(in String sessionId, in PlaybackStateEvent event, int userId);
+ void reportTrackChangeEvent(in String sessionId, in TrackChangeEvent event, int userId);
}
\ No newline at end of file
diff --git a/media/java/android/media/metrics/PlaybackMetrics.java b/media/java/android/media/metrics/PlaybackMetrics.java
index 82a5803..070b4e4 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 @@
* @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 int getStreamSourceType() {
- return mStreamSourceType;
+ 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;
+ }
+
+ /**
+ * 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 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 @@
/** @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[] newArray(int size) {
+ return new PlaybackMetrics[size];
+ }
- @Override
- public PlaybackMetrics createFromParcel(@NonNull Parcel in) {
- return new PlaybackMetrics(in);
- }
- };
+ @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 95f64ea..f48ffe7 100644
--- a/media/java/android/media/metrics/PlaybackMetricsManager.java
+++ b/media/java/android/media/metrics/PlaybackMetricsManager.java
@@ -61,6 +61,30 @@
}
/**
+ * Reports playback state event.
+ * @hide
+ */
+ public void reportPlaybackStateEvent(@NonNull String sessionId, PlaybackStateEvent event) {
+ try {
+ mService.reportPlaybackStateEvent(sessionId, event, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Reports track change event.
+ * @hide
+ */
+ public void reportTrackChangeEvent(@NonNull String sessionId, TrackChangeEvent event) {
+ try {
+ mService.reportTrackChangeEvent(sessionId, event, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Creates a playback session.
*/
public PlaybackSession createSession() {
diff --git a/media/java/android/media/metrics/PlaybackSession.java b/media/java/android/media/metrics/PlaybackSession.java
index 3e2f4e1..0a77516 100644
--- a/media/java/android/media/metrics/PlaybackSession.java
+++ b/media/java/android/media/metrics/PlaybackSession.java
@@ -64,6 +64,20 @@
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/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl b/media/java/android/media/metrics/PlaybackStateEvent.aidl
similarity index 64%
copy from media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
copy to media/java/android/media/metrics/PlaybackStateEvent.aidl
index edf96dd..8b8d05b 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
+++ b/media/java/android/media/metrics/PlaybackStateEvent.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,6 @@
* limitations under the License.
*/
-package android.media.tv.tunerresourcemanager;
+package android.media.metrics;
-/**
- * Simple container of the FrontendInfo struct defined in the TunerHAL 1.0 interface.
- *
- * @hide
- */
-parcelable TunerFrontendInfo {
- int handle;
-
- int frontendType;
-
- int exclusiveGroupId;
-}
+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 0000000..6ce5bf0
--- /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/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl b/media/java/android/media/metrics/TrackChangeEvent.aidl
similarity index 64%
copy from media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
copy to media/java/android/media/metrics/TrackChangeEvent.aidl
index edf96dd..8fbe4a9 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
+++ b/media/java/android/media/metrics/TrackChangeEvent.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,6 @@
* limitations under the License.
*/
-package android.media.tv.tunerresourcemanager;
+package android.media.metrics;
-/**
- * Simple container of the FrontendInfo struct defined in the TunerHAL 1.0 interface.
- *
- * @hide
- */
-parcelable TunerFrontendInfo {
- int handle;
-
- int frontendType;
-
- int exclusiveGroupId;
-}
+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 0000000..fff0e36
--- /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 b12f7c5..1249e0d 100644
--- a/media/java/android/media/tv/TvInputHardwareInfo.java
+++ b/media/java/android/media/tv/TvInputHardwareInfo.java
@@ -188,6 +188,17 @@
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 46b29f5..7192c07 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -988,9 +988,11 @@
}
/**
- * 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 @@
}
/**
- * 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 02390bb..c38d919 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 @@
local_include_dir: "aidl",
backend: {
java: {
- sdk_version: "current",
+ enabled: true,
},
cpp: {
enabled: true,
@@ -33,4 +24,5 @@
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 6f7adbc..e399fbd 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.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 a1f6687..483d972 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 798bf6e..4f27b197 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -27,10 +27,14 @@
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 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 @@
return MtpConstants.RESPONSE_OK;
}
+ @VisibleForNative
+ private int openFilePath(String path, boolean transcode) {
+ Uri uri = MediaStore.scanFile(mContext.getContentResolver(), new File(path));
+ if (uri == null) {
+ Log.i(TAG, "Failed to obtain URI for openFile with transcode support: " + path);
+ return -1;
+ }
+
+ try {
+ Log.i(TAG, "openFile with transcode support: " + path);
+ // TODO(b/158466651): Pass the |transcode| variable as flag to openFile
+ Bundle bundle = null;
+ if (!transcode) {
+ bundle = new Bundle();
+ bundle.putParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES,
+ new ApplicationMediaCapabilities.Builder().addSupportedVideoMimeType(
+ MediaFormat.MIMETYPE_VIDEO_HEVC).build());
+ }
+ return mMediaProvider.openTypedAssetFileDescriptor(uri, "*/*", bundle)
+ .getParcelFileDescriptor().detachFd();
+ } catch (RemoteException | FileNotFoundException e) {
+ Log.w(TAG, "Failed to openFile with transcode support: " + path, e);
+ return -1;
+ }
+ }
+
private int getObjectFormat(int handle) {
MtpStorageManager.MtpObject obj = mManager.getObject(handle);
if (obj == null) {
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 17189fd..4efdcac 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -63,6 +63,7 @@
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 @@
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 @@
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 @@
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 a26753d..f5e3524 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 @@
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 @@
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 @@
}
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 @@
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 a3d2d02c..8a1181a 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 @@
sp<ILnb> openHidlLnbByName(string name, LnbId& lnbId);
sp<IDescrambler> openHidlDescrambler();
vector<int> getLnbHandles();
- FrontendInfo FrontendInfoAidlToHidl(TunerServiceFrontendInfo aidlFrontendInfo);
+ FrontendInfo FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo);
void updateTunerResources();
void updateFrontendResources();
void updateLnbResources();
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index b9aefdd..3751564 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -89,6 +89,13 @@
asan_options: [
"detect_odr_violation=1",
],
+ hwasan_options: [
+ // Image decoders may attempt to allocate a large amount of memory
+ // (especially if the encoded image is large). This doesn't
+ // necessarily mean there is a bug. Set allocator_may_return_null=1
+ // for hwasan so the fuzzer can continue running.
+ "allocator_may_return_null = 1",
+ ],
},
corpus: ["corpus/*"],
host_supported: true,
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index eab5f41..385e455 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -353,7 +353,7 @@
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 d366a0b..cdf4851 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 68e816a..a03ea0df 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 184837f..970c46b 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 116d63b..477844c 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 bc719a3..f10c639 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 a0e5298..e8542f3 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 96a513d..13be6f2 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 3775dbe..3bda5e6 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 1e8fd0b..d3bc515 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 133aea3..905b306 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 cf26b08..86dc694 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 30a331a..5a31f9b 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 4867a8e..60de2ff 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 a0e5091..2fed1ae 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 a0e5091..2fed1ae 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 a0e5091..2fed1ae 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 a0e5091..2fed1ae 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 5780fb4..4fbb57e 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 78cfa5a..5ca9305 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 3f29da0..357f052 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 30907a2..14c7154 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 9e1ecb1..6bb9620 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 4a1f13f..5a9c1cd 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 ce5e262..b31babd 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 e15639b..08c93a2c 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 1205e46..c95b90e 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 80c0d95..ac95cc6 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 b574ff6..df8451f 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 b271b12..ff1c6c5 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 e865690..194223d 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 2d4fd4a..58bf3cb 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 8698bfb..cc5b989 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 beb1482..4cbefd8 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 b50c56a..b695d9d 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 4ab0abd..300c94f 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 fcfe030..94d6c3e 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 e708004..db13fe7 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 299d586..1363e57 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 35ee34d..c01e235 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 a27bc15..68218dd 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 11510ac..5fd8280 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 6a85c83..bf036ec 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 6fed96b..427ca8f 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 6b071cd..a48c45f 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 4d048cb..7ac20e6 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 b16869d..9c2783c 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 cac087f..26fbb03 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 e390161..f289b37 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 2153770..0c9cdffd 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 b16d045..b07af57 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 40f9d2d..16906f6 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 40f9d2d..16906f6 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 6040adf..187cfbdf 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 9939a82..8dd9a39 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 6e6b029..9e7c02e 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 520aa09..55a47c2 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 f60cab8..159afd5 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 da08d87..fdbbe8e 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 a73cbfa..bfd2516 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 5865ec9..437ae7f 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 f9d6401..9b4a720 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 82616cc..6e785de 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 77a9fff..b727d42 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 6d62642..a93282a 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 2c30ea7..3abe064 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 f68fd92..161d95e 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 c8c4b22..4cce2e8 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 f0234a4..06a1ab6 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 caf2321..12bfcf3 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 5ec64a5..0c583b2 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 b8c2ed0..519f0e8 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 7df0869..7721b54 100644
--- a/packages/CompanionDeviceManager/res/values-zu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml
@@ -19,11 +19,9 @@
<string name="app_label" msgid="4470785958457506021">"Isiphathi sedivayisi esihambisanayo"</string>
<string name="chooser_title" msgid="2262294130493605839">"Khetha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> ezophathwa yi-<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"idivayisi"</string>
- <!-- no translation found for profile_name_watch (576290739483672360) -->
- <skip />
+ <string name="profile_name_watch" msgid="576290739483672360">"buka"</string>
<string name="confirmation_title" msgid="4751119145078041732">"Setha i-<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ukuba iphathe i-<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <!-- no translation found for profile_summary (2009764182871566255) -->
- <skip />
+ <string name="profile_summary" msgid="2009764182871566255">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> iyadingeka ukuphatha i-<xliff:g id="PROFILE_NAME">%2$s</xliff:g> yakho. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
<string name="consent_yes" msgid="4055438216605487056">"Yebo"</string>
<string name="consent_no" msgid="1335543792857823917">"Cha ngiyabonga"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-af/arrays.xml b/packages/SettingsLib/res/values-af/arrays.xml
index 93f00e7..ca409f37 100644
--- a/packages/SettingsLib/res/values-af/arrays.xml
+++ b/packages/SettingsLib/res/values-af/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", aktief (media)"</item>
<item msgid="5001852592115448348">", aktief (foon)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Af"</item>
- <item msgid="7839165897132179888">"64 K"</item>
- <item msgid="2715700596495505626">"256 K"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Af"</item>
<item msgid="4064786181089783077">"64 K"</item>
<item msgid="3052710745383602630">"256 K"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Af"</item>
- <item msgid="4195153527464162486">"64 K per logbuffer"</item>
- <item msgid="7464037639415220106">"256 K per logbuffer"</item>
- <item msgid="8539423820514360724">"1 M per logbuffer"</item>
- <item msgid="1984761927103140651">"4 M per logbuffer"</item>
- <item msgid="7892098981256010498">"16 M per logbuffer"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Af"</item>
<item msgid="6014837961827347618">"Alles"</item>
diff --git a/packages/SettingsLib/res/values-am/arrays.xml b/packages/SettingsLib/res/values-am/arrays.xml
index 18696b1..e941c11 100644
--- a/packages/SettingsLib/res/values-am/arrays.xml
+++ b/packages/SettingsLib/res/values-am/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">"፣ ገቢር (ሚዲያ)"</item>
<item msgid="5001852592115448348">"፣ ገቢር (ስልክ)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"ጠፍቷል"</item>
- <item msgid="7839165897132179888">"64 ኪባ"</item>
- <item msgid="2715700596495505626">"256 ኪባ"</item>
- <item msgid="7099386891713159947">"1 ሜባ"</item>
- <item msgid="6069075827077845520">"4 ሜባ"</item>
- <item msgid="8243549501764402572">"16 ሜባ"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"ጠፍቷል"</item>
<item msgid="4064786181089783077">"64 ኪባ"</item>
<item msgid="3052710745383602630">"256 ኪባ"</item>
<item msgid="3691785423374588514">"1 ሜባ"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"ጠፍቷል"</item>
- <item msgid="4195153527464162486">"64 ኪባ በምዝግብ ማስታወሻ ቋጥ"</item>
- <item msgid="7464037639415220106">"256 ኪባ በምዝግብ ማስታወሻ ቋጥ"</item>
- <item msgid="8539423820514360724">"1 ሜ በምዝግብ ማስታወሻ ቋጥ"</item>
- <item msgid="1984761927103140651">"4 ሜ በምዝግብ ማስታወሻ ቋጥ"</item>
- <item msgid="7892098981256010498">"16 ሜ በምዝግብ ማስታወሻ ቋጥ"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"ጠፍቷል"</item>
<item msgid="6014837961827347618">"ሁሉም"</item>
diff --git a/packages/SettingsLib/res/values-ar/arrays.xml b/packages/SettingsLib/res/values-ar/arrays.xml
index d09b50e..db1d4b4 100644
--- a/packages/SettingsLib/res/values-ar/arrays.xml
+++ b/packages/SettingsLib/res/values-ar/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">"، مُفعَّل (وسائط)"</item>
<item msgid="5001852592115448348">"، مُفعَّل (هاتف)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"إيقاف"</item>
- <item msgid="7839165897132179888">"٦٤ كيلوبايت"</item>
- <item msgid="2715700596495505626">"٢٥٦ كيلوبايت"</item>
- <item msgid="7099386891713159947">"1 ميغابايت"</item>
- <item msgid="6069075827077845520">"٤ ميغابايت"</item>
- <item msgid="8243549501764402572">"١٦ ميغابايت"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"إيقاف"</item>
<item msgid="4064786181089783077">"٦٤ كيلوبايت"</item>
<item msgid="3052710745383602630">"٢٥٦ كيلوبايت"</item>
<item msgid="3691785423374588514">"1 ميغابايت"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"إيقاف"</item>
- <item msgid="4195153527464162486">"٦٤ كيلوبايت لكل ذاكرة تخزين مؤقت للتسجيل"</item>
- <item msgid="7464037639415220106">"٢٥٦ كيلوبايت لكل ذاكرة تخزين مؤقت للتسجيل"</item>
- <item msgid="8539423820514360724">"1 ميغابايت لكل ذاكرة تخزين مؤقت للتسجيل"</item>
- <item msgid="1984761927103140651">"٤ ميغابايت لكل ذاكرة تخزين مؤقت للتسجيل"</item>
- <item msgid="7892098981256010498">"١٦ ميغابايت لكل ذاكرة تخزين مؤقت للتسجيل"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"إيقاف"</item>
<item msgid="6014837961827347618">"الكل"</item>
diff --git a/packages/SettingsLib/res/values-as/arrays.xml b/packages/SettingsLib/res/values-as/arrays.xml
index b619d3b..b2494fb 100644
--- a/packages/SettingsLib/res/values-as/arrays.xml
+++ b/packages/SettingsLib/res/values-as/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", সক্ৰিয় (মিডিয়া)"</item>
<item msgid="5001852592115448348">", সক্ৰিয় (ফ\'ন)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"অফ কৰক"</item>
- <item msgid="7839165897132179888">"৬৪কে."</item>
- <item msgid="2715700596495505626">"২৫৬কে."</item>
- <item msgid="7099386891713159947">"১মি."</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"১৬মি."</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"অফ কৰক"</item>
<item msgid="4064786181089783077">"৬৪কে."</item>
<item msgid="3052710745383602630">"২৫৬কে."</item>
<item msgid="3691785423374588514">"১মি."</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"অফ কৰক"</item>
- <item msgid="4195153527464162486">"প্ৰতিটো লগ বাফাৰত ৬৪কে."</item>
- <item msgid="7464037639415220106">"প্ৰতি লগ বাফাৰত 256K"</item>
- <item msgid="8539423820514360724">"প্ৰতিটো লগ বাফাৰত ১মি."</item>
- <item msgid="1984761927103140651">"প্ৰতিটো লগ বাফাৰত ৪মি."</item>
- <item msgid="7892098981256010498">"প্ৰতিটো লগ বাফাৰত ১৬মি."</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"অফ অৱস্থাত আছে"</item>
<item msgid="6014837961827347618">"সকলো"</item>
diff --git a/packages/SettingsLib/res/values-az/arrays.xml b/packages/SettingsLib/res/values-az/arrays.xml
index 55ec9d8..6ee2b8f 100644
--- a/packages/SettingsLib/res/values-az/arrays.xml
+++ b/packages/SettingsLib/res/values-az/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", aktiv (media)"</item>
<item msgid="5001852592115448348">", aktiv (telefon)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Deaktiv"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Deaktiv"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Deaktiv"</item>
- <item msgid="4195153527464162486">"hər jurnal buferinə 64K"</item>
- <item msgid="7464037639415220106">"hər jurnal buferinə 256K"</item>
- <item msgid="8539423820514360724">"hər jurnal buferinə 1M"</item>
- <item msgid="1984761927103140651">"hər jurnal buferinə 4M"</item>
- <item msgid="7892098981256010498">"hər jurnal buferinə 16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Deaktiv"</item>
<item msgid="6014837961827347618">"Bütün"</item>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
index 2926067..630ad7d 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", aktivan (medijski)"</item>
<item msgid="5001852592115448348">", aktivan (telefon)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Isključeno"</item>
- <item msgid="7839165897132179888">"64 kB"</item>
- <item msgid="2715700596495505626">"256 kB"</item>
- <item msgid="7099386891713159947">"1 MB"</item>
- <item msgid="6069075827077845520">"4 MB"</item>
- <item msgid="8243549501764402572">"16 MB"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Isključeno"</item>
<item msgid="4064786181089783077">"64 kB"</item>
<item msgid="3052710745383602630">"256 kB"</item>
<item msgid="3691785423374588514">"1 MB"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Isključeno"</item>
- <item msgid="4195153527464162486">"64 kB po međumemoriji evidencije"</item>
- <item msgid="7464037639415220106">"256 kB po međumemoriji evidencije"</item>
- <item msgid="8539423820514360724">"1 MB po međumemoriji evidencije"</item>
- <item msgid="1984761927103140651">"4 MB po međumemoriji evidencije"</item>
- <item msgid="7892098981256010498">"16 MB po međumemoriji evidencije"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Isključeno"</item>
<item msgid="6014837961827347618">"Sve"</item>
diff --git a/packages/SettingsLib/res/values-be/arrays.xml b/packages/SettingsLib/res/values-be/arrays.xml
index af3a161..2d2c509 100644
--- a/packages/SettingsLib/res/values-be/arrays.xml
+++ b/packages/SettingsLib/res/values-be/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", уключана (мультымедыя)"</item>
<item msgid="5001852592115448348">", уключана (тэлефон)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Выкл."</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Выкл."</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Выкл."</item>
- <item msgid="4195153527464162486">"64K на буфер журнала"</item>
- <item msgid="7464037639415220106">"256K на буфер журнала"</item>
- <item msgid="8539423820514360724">"1M на буфер журнала"</item>
- <item msgid="1984761927103140651">"4M на буфер журнала"</item>
- <item msgid="7892098981256010498">"16M на буфер журнала"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Выключана"</item>
<item msgid="6014837961827347618">"Усе"</item>
diff --git a/packages/SettingsLib/res/values-bg/arrays.xml b/packages/SettingsLib/res/values-bg/arrays.xml
index 1b25bed..482ec22 100644
--- a/packages/SettingsLib/res/values-bg/arrays.xml
+++ b/packages/SettingsLib/res/values-bg/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">"– активно (мултимедия)"</item>
<item msgid="5001852592115448348">"– активно (телефон)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Изключено"</item>
- <item msgid="7839165897132179888">"64 КБ"</item>
- <item msgid="2715700596495505626">"256 КБ"</item>
- <item msgid="7099386891713159947">"1 МБ"</item>
- <item msgid="6069075827077845520">"4 МБ"</item>
- <item msgid="8243549501764402572">"16 МБ"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Изключено"</item>
<item msgid="4064786181089783077">"64 КБ"</item>
<item msgid="3052710745383602630">"256 КБ"</item>
<item msgid="3691785423374588514">"1 МБ"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Изключено"</item>
- <item msgid="4195153527464162486">"Рег. буфер – 64 КБ"</item>
- <item msgid="7464037639415220106">"Рег. буфер – 256 КБ"</item>
- <item msgid="8539423820514360724">"Рег. буфер – 1 МБ"</item>
- <item msgid="1984761927103140651">"Рег. буфер – 4 МБ"</item>
- <item msgid="7892098981256010498">"Рег. буфер – 16 МБ"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Изкл."</item>
<item msgid="6014837961827347618">"Всички"</item>
diff --git a/packages/SettingsLib/res/values-bn/arrays.xml b/packages/SettingsLib/res/values-bn/arrays.xml
index 34cbc8f..2da3076 100644
--- a/packages/SettingsLib/res/values-bn/arrays.xml
+++ b/packages/SettingsLib/res/values-bn/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", চালু আছে (মিডিয়া)"</item>
<item msgid="5001852592115448348">", চালু আছে (ফোন)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"বন্ধ আছে"</item>
- <item msgid="7839165897132179888">"৬৪K"</item>
- <item msgid="2715700596495505626">"২৫৬K"</item>
- <item msgid="7099386891713159947">"১M"</item>
- <item msgid="6069075827077845520">"৪M"</item>
- <item msgid="8243549501764402572">"১৬M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"বন্ধ আছে"</item>
<item msgid="4064786181089783077">"৬৪K"</item>
<item msgid="3052710745383602630">"২৫৬K"</item>
<item msgid="3691785423374588514">"১M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"বন্ধ আছে"</item>
- <item msgid="4195153527464162486">"লগ বাফার প্রতি ৬৪K"</item>
- <item msgid="7464037639415220106">"লগ বাফার প্রতি ২৫৬K"</item>
- <item msgid="8539423820514360724">"লগ বাফার প্রতি ১M"</item>
- <item msgid="1984761927103140651">"লগ বাফার প্রতি ৪M"</item>
- <item msgid="7892098981256010498">"লগ বাফার প্রতি ১৬M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"বন্ধ আছে"</item>
<item msgid="6014837961827347618">"সমস্ত"</item>
diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml
index 6d2f1f3..b704385 100644
--- a/packages/SettingsLib/res/values-bs/arrays.xml
+++ b/packages/SettingsLib/res/values-bs/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", aktivan (mediji)"</item>
<item msgid="5001852592115448348">", aktivan (telefon)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Isključeno"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Isključeno"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Isključeno"</item>
- <item msgid="4195153527464162486">"64K po međumemoriji zapisnika"</item>
- <item msgid="7464037639415220106">"256k po međumemoriji zapisnika"</item>
- <item msgid="8539423820514360724">"1M po međumemoriji zapisnika"</item>
- <item msgid="1984761927103140651">"4M po međumemoriji zapisnika"</item>
- <item msgid="7892098981256010498">"16M po međumemoriji zapisnika"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Isključeno"</item>
<item msgid="6014837961827347618">"Sve"</item>
diff --git a/packages/SettingsLib/res/values-ca/arrays.xml b/packages/SettingsLib/res/values-ca/arrays.xml
index 4b24637..c9f63ab 100644
--- a/packages/SettingsLib/res/values-ca/arrays.xml
+++ b/packages/SettingsLib/res/values-ca/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", actiu (contingut multimèdia)"</item>
<item msgid="5001852592115448348">", actiu (telèfon)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"No"</item>
- <item msgid="7839165897132179888">"64 K"</item>
- <item msgid="2715700596495505626">"256 K"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"No"</item>
<item msgid="4064786181089783077">"64 K"</item>
<item msgid="3052710745383602630">"256 K"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"No"</item>
- <item msgid="4195153527464162486">"64 K / memòria intermèdia del registre"</item>
- <item msgid="7464037639415220106">"256 K / memòria intermèdia del registre"</item>
- <item msgid="8539423820514360724">"1 M / memòria intermèdia reg."</item>
- <item msgid="1984761927103140651">"4 M / memòria intermèdia del registre"</item>
- <item msgid="7892098981256010498">"16 M / memòria intermèdia del registre"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Desactivat"</item>
<item msgid="6014837961827347618">"Tot"</item>
diff --git a/packages/SettingsLib/res/values-cs/arrays.xml b/packages/SettingsLib/res/values-cs/arrays.xml
index 27dce16..556fc10 100644
--- a/packages/SettingsLib/res/values-cs/arrays.xml
+++ b/packages/SettingsLib/res/values-cs/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", aktivní (média)"</item>
<item msgid="5001852592115448348">", aktivní (telefon)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Vypnuto"</item>
- <item msgid="7839165897132179888">"64 kB"</item>
- <item msgid="2715700596495505626">"256 kB"</item>
- <item msgid="7099386891713159947">"1 MB"</item>
- <item msgid="6069075827077845520">"4 MB"</item>
- <item msgid="8243549501764402572">"16 MB"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Vypnuto"</item>
<item msgid="4064786181089783077">"64 kB"</item>
<item msgid="3052710745383602630">"256 kB"</item>
<item msgid="3691785423374588514">"1 MB"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Vypnuto"</item>
- <item msgid="4195153527464162486">"64 kB na vyrovnávací paměť protokolů"</item>
- <item msgid="7464037639415220106">"256 kB na vyrovnávací paměť protokolů"</item>
- <item msgid="8539423820514360724">"1 MB na vyrovnávací paměť protokolů"</item>
- <item msgid="1984761927103140651">"4 MB na vyrovnávací paměť protokolů"</item>
- <item msgid="7892098981256010498">"16 MB na vyrovnávací paměť protokolů"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Vypnuto"</item>
<item msgid="6014837961827347618">"Vše"</item>
diff --git a/packages/SettingsLib/res/values-da/arrays.xml b/packages/SettingsLib/res/values-da/arrays.xml
index efe4150..69b8a09 100644
--- a/packages/SettingsLib/res/values-da/arrays.xml
+++ b/packages/SettingsLib/res/values-da/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", aktiv (medier)"</item>
<item msgid="5001852592115448348">", aktiv (telefon)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Fra"</item>
- <item msgid="7839165897132179888">"64 kB"</item>
- <item msgid="2715700596495505626">"256 kB"</item>
- <item msgid="7099386891713159947">"1 MB"</item>
- <item msgid="6069075827077845520">"4 MB"</item>
- <item msgid="8243549501764402572">"16 MB"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Fra"</item>
<item msgid="4064786181089783077">"64 kB"</item>
<item msgid="3052710745383602630">"256 kB"</item>
<item msgid="3691785423374588514">"1 MB"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Fra"</item>
- <item msgid="4195153527464162486">"64 kB pr. logbuffer"</item>
- <item msgid="7464037639415220106">"256 kB pr. logbuffer"</item>
- <item msgid="8539423820514360724">"1 MB pr. logbuffer"</item>
- <item msgid="1984761927103140651">"4 MB pr. logbuffer"</item>
- <item msgid="7892098981256010498">"16 MB pr. logbuffer"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Fra"</item>
<item msgid="6014837961827347618">"Alle"</item>
diff --git a/packages/SettingsLib/res/values-de/arrays.xml b/packages/SettingsLib/res/values-de/arrays.xml
index e7c4887..f6e3496 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 16af032..ba760dd 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 838ca79..2fc7d0b 100644
--- a/packages/SettingsLib/res/values-el/arrays.xml
+++ b/packages/SettingsLib/res/values-el/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", ενεργή (μέσα)"</item>
<item msgid="5001852592115448348">", ενεργή (τηλέφωνο)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Ανενεργό"</item>
- <item msgid="7839165897132179888">"64 K"</item>
- <item msgid="2715700596495505626">"256 K"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Ανενεργό"</item>
<item msgid="4064786181089783077">"64 K"</item>
<item msgid="3052710745383602630">"256 K"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Ανενεργό"</item>
- <item msgid="4195153527464162486">"64 K ανά πρ. μν. αρχ. καταγρ."</item>
- <item msgid="7464037639415220106">"256 K ανά πρ. μν. αρχ. καταγρ."</item>
- <item msgid="8539423820514360724">"1 Μ ανά προσ. μν. αρχ. καταγρ."</item>
- <item msgid="1984761927103140651">"4 M ανά προσ. μν. αρχ. καταγρ."</item>
- <item msgid="7892098981256010498">"16 M ανά πρ. μν. αρχ. καταγρ."</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Ανενεργό"</item>
<item msgid="6014837961827347618">"Όλα"</item>
diff --git a/packages/SettingsLib/res/values-en-rAU/arrays.xml b/packages/SettingsLib/res/values-en-rAU/arrays.xml
index 6569f18..31dc7c9 100644
--- a/packages/SettingsLib/res/values-en-rAU/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rAU/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", active (media)"</item>
<item msgid="5001852592115448348">", active (phone)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Off"</item>
- <item msgid="7839165897132179888">"64 K"</item>
- <item msgid="2715700596495505626">"256 K"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Off"</item>
<item msgid="4064786181089783077">"64 K"</item>
<item msgid="3052710745383602630">"256 K"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Off"</item>
- <item msgid="4195153527464162486">"64 K per log buffer"</item>
- <item msgid="7464037639415220106">"256 K per log buffer"</item>
- <item msgid="8539423820514360724">"1 M per log buffer"</item>
- <item msgid="1984761927103140651">"4 M per log buffer"</item>
- <item msgid="7892098981256010498">"16 M per log buffer"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Off"</item>
<item msgid="6014837961827347618">"All"</item>
diff --git a/packages/SettingsLib/res/values-en-rCA/arrays.xml b/packages/SettingsLib/res/values-en-rCA/arrays.xml
index 6569f18..31dc7c9 100644
--- a/packages/SettingsLib/res/values-en-rCA/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rCA/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", active (media)"</item>
<item msgid="5001852592115448348">", active (phone)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Off"</item>
- <item msgid="7839165897132179888">"64 K"</item>
- <item msgid="2715700596495505626">"256 K"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Off"</item>
<item msgid="4064786181089783077">"64 K"</item>
<item msgid="3052710745383602630">"256 K"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Off"</item>
- <item msgid="4195153527464162486">"64 K per log buffer"</item>
- <item msgid="7464037639415220106">"256 K per log buffer"</item>
- <item msgid="8539423820514360724">"1 M per log buffer"</item>
- <item msgid="1984761927103140651">"4 M per log buffer"</item>
- <item msgid="7892098981256010498">"16 M per log buffer"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Off"</item>
<item msgid="6014837961827347618">"All"</item>
diff --git a/packages/SettingsLib/res/values-en-rGB/arrays.xml b/packages/SettingsLib/res/values-en-rGB/arrays.xml
index 6569f18..31dc7c9 100644
--- a/packages/SettingsLib/res/values-en-rGB/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rGB/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", active (media)"</item>
<item msgid="5001852592115448348">", active (phone)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Off"</item>
- <item msgid="7839165897132179888">"64 K"</item>
- <item msgid="2715700596495505626">"256 K"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Off"</item>
<item msgid="4064786181089783077">"64 K"</item>
<item msgid="3052710745383602630">"256 K"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Off"</item>
- <item msgid="4195153527464162486">"64 K per log buffer"</item>
- <item msgid="7464037639415220106">"256 K per log buffer"</item>
- <item msgid="8539423820514360724">"1 M per log buffer"</item>
- <item msgid="1984761927103140651">"4 M per log buffer"</item>
- <item msgid="7892098981256010498">"16 M per log buffer"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Off"</item>
<item msgid="6014837961827347618">"All"</item>
diff --git a/packages/SettingsLib/res/values-en-rIN/arrays.xml b/packages/SettingsLib/res/values-en-rIN/arrays.xml
index 6569f18..31dc7c9 100644
--- a/packages/SettingsLib/res/values-en-rIN/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rIN/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", active (media)"</item>
<item msgid="5001852592115448348">", active (phone)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Off"</item>
- <item msgid="7839165897132179888">"64 K"</item>
- <item msgid="2715700596495505626">"256 K"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Off"</item>
<item msgid="4064786181089783077">"64 K"</item>
<item msgid="3052710745383602630">"256 K"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Off"</item>
- <item msgid="4195153527464162486">"64 K per log buffer"</item>
- <item msgid="7464037639415220106">"256 K per log buffer"</item>
- <item msgid="8539423820514360724">"1 M per log buffer"</item>
- <item msgid="1984761927103140651">"4 M per log buffer"</item>
- <item msgid="7892098981256010498">"16 M per log buffer"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Off"</item>
<item msgid="6014837961827347618">"All"</item>
diff --git a/packages/SettingsLib/res/values-en-rXC/arrays.xml b/packages/SettingsLib/res/values-en-rXC/arrays.xml
index cb702fe..e4322b9 100644
--- a/packages/SettingsLib/res/values-en-rXC/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rXC/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", active (media)"</item>
<item msgid="5001852592115448348">", active (phone)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Off"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Off"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Off"</item>
- <item msgid="4195153527464162486">"64K per log buffer"</item>
- <item msgid="7464037639415220106">"256K per log buffer"</item>
- <item msgid="8539423820514360724">"1M per log buffer"</item>
- <item msgid="1984761927103140651">"4M per log buffer"</item>
- <item msgid="7892098981256010498">"16M per log buffer"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Off"</item>
<item msgid="6014837961827347618">"All"</item>
diff --git a/packages/SettingsLib/res/values-es-rUS/arrays.xml b/packages/SettingsLib/res/values-es-rUS/arrays.xml
index cfce9b6..cf6ce2b 100644
--- a/packages/SettingsLib/res/values-es-rUS/arrays.xml
+++ b/packages/SettingsLib/res/values-es-rUS/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", activo (contenido multimedia)"</item>
<item msgid="5001852592115448348">", activo (teléfono)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Desactivado"</item>
- <item msgid="7839165897132179888">"64 K"</item>
- <item msgid="2715700596495505626">"256 K"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Desactivado"</item>
<item msgid="4064786181089783077">"64 K"</item>
<item msgid="3052710745383602630">"256 K"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Desactivado"</item>
- <item msgid="4195153527464162486">"64 K/búfer registro"</item>
- <item msgid="7464037639415220106">"256 K/búfer registro"</item>
- <item msgid="8539423820514360724">"1 M/búfer registro"</item>
- <item msgid="1984761927103140651">"4 M/búfer registro"</item>
- <item msgid="7892098981256010498">"16 M/búfer registro"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Desactivado"</item>
<item msgid="6014837961827347618">"Todo"</item>
diff --git a/packages/SettingsLib/res/values-es/arrays.xml b/packages/SettingsLib/res/values-es/arrays.xml
index 5682dd5..37f91b2 100644
--- a/packages/SettingsLib/res/values-es/arrays.xml
+++ b/packages/SettingsLib/res/values-es/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", activo (contenido multimedia)"</item>
<item msgid="5001852592115448348">", activo (teléfono)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Desactivado"</item>
- <item msgid="7839165897132179888">"64 K"</item>
- <item msgid="2715700596495505626">"256 K"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Desactivado"</item>
<item msgid="4064786181089783077">"64 K"</item>
<item msgid="3052710745383602630">"256 K"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Desactivado"</item>
- <item msgid="4195153527464162486">"64 K/búfer registro"</item>
- <item msgid="7464037639415220106">"256 K/búfer registro"</item>
- <item msgid="8539423820514360724">"1 M/búfer registro"</item>
- <item msgid="1984761927103140651">"4 M/búfer registro"</item>
- <item msgid="7892098981256010498">"16 M/búfer registro"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Desactivado"</item>
<item msgid="6014837961827347618">"Todo"</item>
diff --git a/packages/SettingsLib/res/values-et/arrays.xml b/packages/SettingsLib/res/values-et/arrays.xml
index 9015cfe..537ea7a 100644
--- a/packages/SettingsLib/res/values-et/arrays.xml
+++ b/packages/SettingsLib/res/values-et/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", aktiivne (meedia)"</item>
<item msgid="5001852592115448348">", aktiivne (telefon)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Väljas"</item>
- <item msgid="7839165897132179888">"64 000"</item>
- <item msgid="2715700596495505626">"256 000"</item>
- <item msgid="7099386891713159947">"1 000 000"</item>
- <item msgid="6069075827077845520">"4 000 000"</item>
- <item msgid="8243549501764402572">"16 000 000"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Väljas"</item>
<item msgid="4064786181089783077">"64 000"</item>
<item msgid="3052710745383602630">"256 000"</item>
<item msgid="3691785423374588514">"1 000 000"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Väljas"</item>
- <item msgid="4195153527464162486">"64 000 / logipuhver"</item>
- <item msgid="7464037639415220106">"256 000 / logipuhver"</item>
- <item msgid="8539423820514360724">"1 000 000 / logipuhver"</item>
- <item msgid="1984761927103140651">"4 000 000 / logipuhver"</item>
- <item msgid="7892098981256010498">"16 000 000 / logipuhver"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Väljas"</item>
<item msgid="6014837961827347618">"Kõik"</item>
diff --git a/packages/SettingsLib/res/values-eu/arrays.xml b/packages/SettingsLib/res/values-eu/arrays.xml
index 0e94bba..367d31c 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 c7b376a..2582854 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 075f7e0..070c8ec 100644
--- a/packages/SettingsLib/res/values-fa/arrays.xml
+++ b/packages/SettingsLib/res/values-fa/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">"، فعال (رسانه)"</item>
<item msgid="5001852592115448348">"، فعال (تلفن)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"خاموش"</item>
- <item msgid="7839165897132179888">"۶۴ هزار"</item>
- <item msgid="2715700596495505626">"۲۵۶ هزار"</item>
- <item msgid="7099386891713159947">"۱ میلیون"</item>
- <item msgid="6069075827077845520">"۴ میلیون"</item>
- <item msgid="8243549501764402572">"۱۶ میلیون"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"خاموش"</item>
<item msgid="4064786181089783077">"۶۴ هزار"</item>
<item msgid="3052710745383602630">"۲۵۶ هزار"</item>
<item msgid="3691785423374588514">"۱ میلیون"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"خاموش"</item>
- <item msgid="4195153527464162486">"۶۴ هزار در هر بافر گزارش"</item>
- <item msgid="7464037639415220106">"۲۵۶ هزار در هر بافر گزارش"</item>
- <item msgid="8539423820514360724">"۱ میلیون در هر بافر گزارش"</item>
- <item msgid="1984761927103140651">"۴ میلیون در هر بافر گزارش"</item>
- <item msgid="7892098981256010498">"۱۶ میلیون در هر بافر گزارش"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"خاموش"</item>
<item msgid="6014837961827347618">"همه"</item>
diff --git a/packages/SettingsLib/res/values-fi/arrays.xml b/packages/SettingsLib/res/values-fi/arrays.xml
index d233c56..6c38cdf 100644
--- a/packages/SettingsLib/res/values-fi/arrays.xml
+++ b/packages/SettingsLib/res/values-fi/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", aktiivinen (media)"</item>
<item msgid="5001852592115448348">", aktiivinen (puhelin)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Ei päällä"</item>
- <item msgid="7839165897132179888">"64 kt"</item>
- <item msgid="2715700596495505626">"256 kt"</item>
- <item msgid="7099386891713159947">"1 Mt"</item>
- <item msgid="6069075827077845520">"4 Mt"</item>
- <item msgid="8243549501764402572">"16 Mt"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Ei päällä"</item>
<item msgid="4064786181089783077">"64 kt"</item>
<item msgid="3052710745383602630">"256 kt"</item>
<item msgid="3691785423374588514">"1 Mt"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Ei päällä"</item>
- <item msgid="4195153527464162486">"64 kt / lokipuskuri"</item>
- <item msgid="7464037639415220106">"256 kt / lokipuskuri"</item>
- <item msgid="8539423820514360724">"1 Mt / lokipuskuri"</item>
- <item msgid="1984761927103140651">"4 Mt / lokipuskuri"</item>
- <item msgid="7892098981256010498">"16 Mt / lokipuskuri"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Ei päällä"</item>
<item msgid="6014837961827347618">"Kaikki"</item>
diff --git a/packages/SettingsLib/res/values-fr-rCA/arrays.xml b/packages/SettingsLib/res/values-fr-rCA/arrays.xml
index 1db6540..8d48047 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 58ea795..e24c130 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 7660925..d65ba69 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 f321ea3..a79ed0c 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 98f2072..f13eaae 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 254958f..97662a6 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 1a3bf98..0bbd4f6 100644
--- a/packages/SettingsLib/res/values-gu/arrays.xml
+++ b/packages/SettingsLib/res/values-gu/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", સક્રિય (મીડિયા)"</item>
<item msgid="5001852592115448348">", સક્રિય (ફોન)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"બંધ"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"બંધ"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"બંધ"</item>
- <item msgid="4195153527464162486">"લૉગ બફર દીઠ 64K"</item>
- <item msgid="7464037639415220106">"લૉગ બફર દીઠ 256K"</item>
- <item msgid="8539423820514360724">"લૉગ બફર દીઠ 1M"</item>
- <item msgid="1984761927103140651">"લૉગ બફર દીઠ 4M"</item>
- <item msgid="7892098981256010498">"લૉગ બફર દીઠ 16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"બંધ"</item>
<item msgid="6014837961827347618">"તમામ"</item>
diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml
index 36b16e6..ace8b4a 100644
--- a/packages/SettingsLib/res/values-hi/arrays.xml
+++ b/packages/SettingsLib/res/values-hi/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", चालू है (सिर्फ़ मीडिया के लिए)"</item>
<item msgid="5001852592115448348">", चालू है (सिर्फ़ फ़ोन के लिए)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"बंद"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"बंद"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"बंद"</item>
- <item msgid="4195153527464162486">"64K प्रति लॉग बफ़र"</item>
- <item msgid="7464037639415220106">"256K प्रति लॉग बफ़र"</item>
- <item msgid="8539423820514360724">"1 एमबी प्रति लॉग बफ़र"</item>
- <item msgid="1984761927103140651">"4M प्रति लॉग बफ़र"</item>
- <item msgid="7892098981256010498">"16M प्रति लॉग बफ़र"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"बंद"</item>
<item msgid="6014837961827347618">"सभी"</item>
diff --git a/packages/SettingsLib/res/values-hr/arrays.xml b/packages/SettingsLib/res/values-hr/arrays.xml
index 82a1e4a..c4188bf 100644
--- a/packages/SettingsLib/res/values-hr/arrays.xml
+++ b/packages/SettingsLib/res/values-hr/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", aktivno (mediji)"</item>
<item msgid="5001852592115448348">", aktivno (telefon)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Isključeno"</item>
- <item msgid="7839165897132179888">"64 KB"</item>
- <item msgid="2715700596495505626">"256 KB"</item>
- <item msgid="7099386891713159947">"1 MB"</item>
- <item msgid="6069075827077845520">"4 MB"</item>
- <item msgid="8243549501764402572">"16 MB"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Isključeno"</item>
<item msgid="4064786181089783077">"64 KB"</item>
<item msgid="3052710745383602630">"256 KB"</item>
<item msgid="3691785423374588514">"1 MB"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Isključeno"</item>
- <item msgid="4195153527464162486">"64 KB po međusprem. zapisnika"</item>
- <item msgid="7464037639415220106">"256 KB po međusprem. zapisnika"</item>
- <item msgid="8539423820514360724">"1 MB po međusprem. zapisnika"</item>
- <item msgid="1984761927103140651">"4 MB po međusprem. zapisnika"</item>
- <item msgid="7892098981256010498">"16 MB po međusprem. zapisnika"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Isključeno"</item>
<item msgid="6014837961827347618">"Sve"</item>
diff --git a/packages/SettingsLib/res/values-hu/arrays.xml b/packages/SettingsLib/res/values-hu/arrays.xml
index d043af0..cc36c18 100644
--- a/packages/SettingsLib/res/values-hu/arrays.xml
+++ b/packages/SettingsLib/res/values-hu/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", aktív (média)"</item>
<item msgid="5001852592115448348">", aktív (telefon)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Ki"</item>
- <item msgid="7839165897132179888">"64 KB"</item>
- <item msgid="2715700596495505626">"256 KB"</item>
- <item msgid="7099386891713159947">"1 MB"</item>
- <item msgid="6069075827077845520">"4 MB"</item>
- <item msgid="8243549501764402572">"16 MB"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Ki"</item>
<item msgid="4064786181089783077">"64 KB"</item>
<item msgid="3052710745383602630">"256 KB"</item>
<item msgid="3691785423374588514">"1 MB"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Ki"</item>
- <item msgid="4195153527464162486">"64 KB/naplópuffer"</item>
- <item msgid="7464037639415220106">"256 KB/naplópuffer"</item>
- <item msgid="8539423820514360724">"1 MB/naplópuffer"</item>
- <item msgid="1984761927103140651">"4 MB/naplópuffer"</item>
- <item msgid="7892098981256010498">"16 MB/naplópuffer"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Kikapcsolva"</item>
<item msgid="6014837961827347618">"Összes"</item>
diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml
index a279872..76ed9bd 100644
--- a/packages/SettingsLib/res/values-hy/arrays.xml
+++ b/packages/SettingsLib/res/values-hy/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", ակտիվ է (մեդիա)"</item>
<item msgid="5001852592115448348">", ակտիվ է (հեռախոս)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Անջատված է"</item>
- <item msgid="7839165897132179888">"64ԿԲ"</item>
- <item msgid="2715700596495505626">"256ԿԲ"</item>
- <item msgid="7099386891713159947">"1ՄԲ"</item>
- <item msgid="6069075827077845520">"4ՄԲ"</item>
- <item msgid="8243549501764402572">"16ՄԲ"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Անջատված է"</item>
<item msgid="4064786181089783077">"64ԿԲ"</item>
<item msgid="3052710745383602630">"256ԿԲ"</item>
<item msgid="3691785423374588514">"1ՄԲ"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Անջատված է"</item>
- <item msgid="4195153527464162486">"Բուֆեր՝ առավ. 64ԿԲ"</item>
- <item msgid="7464037639415220106">"Բուֆեր՝ առավ. 256ԿԲ"</item>
- <item msgid="8539423820514360724">"Բուֆեր՝ առավ. 1ՄԲ"</item>
- <item msgid="1984761927103140651">"Բուֆեր՝ առավ. 4ՄԲ"</item>
- <item msgid="7892098981256010498">"Բուֆեր՝ առավ. 16ՄԲ"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Անջատված է"</item>
<item msgid="6014837961827347618">"Բոլորը"</item>
diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml
index 3ab50cc..14e3313 100644
--- a/packages/SettingsLib/res/values-in/arrays.xml
+++ b/packages/SettingsLib/res/values-in/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", aktif (media)"</item>
<item msgid="5001852592115448348">", aktif (ponsel)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Nonaktif"</item>
- <item msgid="7839165897132179888">"64 K"</item>
- <item msgid="2715700596495505626">"256 K"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Nonaktif"</item>
<item msgid="4064786181089783077">"64 K"</item>
<item msgid="3052710745383602630">"256 K"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Nonaktif"</item>
- <item msgid="4195153527464162486">"64 K/buffer log"</item>
- <item msgid="7464037639415220106">"256 K/buffer log"</item>
- <item msgid="8539423820514360724">"1 M/buffer log"</item>
- <item msgid="1984761927103140651">"4 M/buffer log"</item>
- <item msgid="7892098981256010498">"16 M/buffer log"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Nonaktif"</item>
<item msgid="6014837961827347618">"Semua"</item>
diff --git a/packages/SettingsLib/res/values-is/arrays.xml b/packages/SettingsLib/res/values-is/arrays.xml
index 93274e4..7c1773b 100644
--- a/packages/SettingsLib/res/values-is/arrays.xml
+++ b/packages/SettingsLib/res/values-is/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", virkt (hljóð- og myndefni)"</item>
<item msgid="5001852592115448348">", virkt (sími)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Slökkt"</item>
- <item msgid="7839165897132179888">"64 k"</item>
- <item msgid="2715700596495505626">"256 k"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Slökkt"</item>
<item msgid="4064786181089783077">"64 k"</item>
<item msgid="3052710745383602630">"256 k"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Slökkt"</item>
- <item msgid="4195153527464162486">"64 k/biðminni"</item>
- <item msgid="7464037639415220106">"256 k/biðminni"</item>
- <item msgid="8539423820514360724">"1 M/biðminni"</item>
- <item msgid="1984761927103140651">"4 M/biðminni"</item>
- <item msgid="7892098981256010498">"16 M/biðminni"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Slökkt"</item>
<item msgid="6014837961827347618">"Allt"</item>
diff --git a/packages/SettingsLib/res/values-it/arrays.xml b/packages/SettingsLib/res/values-it/arrays.xml
index 57c0c9b..127903f 100644
--- a/packages/SettingsLib/res/values-it/arrays.xml
+++ b/packages/SettingsLib/res/values-it/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", attivo (contenuti multimediali)"</item>
<item msgid="5001852592115448348">", attivo (telefono)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Off"</item>
- <item msgid="7839165897132179888">"64 kB"</item>
- <item msgid="2715700596495505626">"256 kB"</item>
- <item msgid="7099386891713159947">"1 MB"</item>
- <item msgid="6069075827077845520">"4 MB"</item>
- <item msgid="8243549501764402572">"16 MB"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Off"</item>
<item msgid="4064786181089783077">"64 kB"</item>
<item msgid="3052710745383602630">"256 kB"</item>
<item msgid="3691785423374588514">"1 MB"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Off"</item>
- <item msgid="4195153527464162486">"64 kB/buffer log"</item>
- <item msgid="7464037639415220106">"256 kB/buffer log"</item>
- <item msgid="8539423820514360724">"1 MB/buffer log"</item>
- <item msgid="1984761927103140651">"4 MB/buffer log"</item>
- <item msgid="7892098981256010498">"16 MB/buffer log"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Off"</item>
<item msgid="6014837961827347618">"Tutti"</item>
diff --git a/packages/SettingsLib/res/values-iw/arrays.xml b/packages/SettingsLib/res/values-iw/arrays.xml
index fa53ab8..151b825 100644
--- a/packages/SettingsLib/res/values-iw/arrays.xml
+++ b/packages/SettingsLib/res/values-iw/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", פעיל (מדיה)"</item>
<item msgid="5001852592115448348">", פעיל (טלפון)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"כבוי"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"כבוי"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"כבוי"</item>
- <item msgid="4195153527464162486">"64K לכל מאגר של יומן רישום"</item>
- <item msgid="7464037639415220106">"256K לכל מאגר של יומן רישום"</item>
- <item msgid="8539423820514360724">"1M לכל מאגר של יומן רישום"</item>
- <item msgid="1984761927103140651">"4M לכל מאגר של יומן רישום"</item>
- <item msgid="7892098981256010498">"16M לכל מאגר של יומן רישום"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"כבוי"</item>
<item msgid="6014837961827347618">"הכול"</item>
diff --git a/packages/SettingsLib/res/values-ja/arrays.xml b/packages/SettingsLib/res/values-ja/arrays.xml
index 7a4e71b..1401069 100644
--- a/packages/SettingsLib/res/values-ja/arrays.xml
+++ b/packages/SettingsLib/res/values-ja/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">"、有効(メディア)"</item>
<item msgid="5001852592115448348">"、有効(スマートフォン)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"OFF"</item>
- <item msgid="7839165897132179888">"64 K"</item>
- <item msgid="2715700596495505626">"256 K"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"OFF"</item>
<item msgid="4064786181089783077">"64 K"</item>
<item msgid="3052710745383602630">"256 K"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"OFF"</item>
- <item msgid="4195153527464162486">"64 K / ログバッファ"</item>
- <item msgid="7464037639415220106">"256 K / ログバッファ"</item>
- <item msgid="8539423820514360724">"1 M / ログバッファ"</item>
- <item msgid="1984761927103140651">"4 M / ログバッファ"</item>
- <item msgid="7892098981256010498">"16 M / ログバッファ"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"OFF"</item>
<item msgid="6014837961827347618">"すべて"</item>
diff --git a/packages/SettingsLib/res/values-ka/arrays.xml b/packages/SettingsLib/res/values-ka/arrays.xml
index 935cc46..62ae1e6 100644
--- a/packages/SettingsLib/res/values-ka/arrays.xml
+++ b/packages/SettingsLib/res/values-ka/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", აქტიური (მედია)"</item>
<item msgid="5001852592115448348">", აქტიური (ტელეფონი)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"გამორთული"</item>
- <item msgid="7839165897132179888">"64 კბაიტი"</item>
- <item msgid="2715700596495505626">"256 კბაიტი"</item>
- <item msgid="7099386891713159947">"1 მბაიტი"</item>
- <item msgid="6069075827077845520">"4 მბაიტი"</item>
- <item msgid="8243549501764402572">"16 მბაიტი"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"გამორთული"</item>
<item msgid="4064786181089783077">"64 კბაიტი"</item>
<item msgid="3052710745383602630">"256 კბაიტი"</item>
<item msgid="3691785423374588514">"1 მბაიტი"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"გამორთული"</item>
- <item msgid="4195153527464162486">"64 კბაიტი / ჟურნალის ბუფერი"</item>
- <item msgid="7464037639415220106">"256 კბაიტი / ჟურნალის ბუფერი"</item>
- <item msgid="8539423820514360724">"1 მბაიტი / ჟურნალის ბუფერი"</item>
- <item msgid="1984761927103140651">"4 მბაიტი / ჟურნალის ბუფერი"</item>
- <item msgid="7892098981256010498">"16 მბაიტი / ჟურნალის ბუფერი"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"გამორთული"</item>
<item msgid="6014837961827347618">"ყველა"</item>
diff --git a/packages/SettingsLib/res/values-kk/arrays.xml b/packages/SettingsLib/res/values-kk/arrays.xml
index faa8af8..a2fe014 100644
--- a/packages/SettingsLib/res/values-kk/arrays.xml
+++ b/packages/SettingsLib/res/values-kk/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", қосулы (медиамазмұн)"</item>
<item msgid="5001852592115448348">", қосулы (телефон)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Өшірулі"</item>
- <item msgid="7839165897132179888">"64 КБ"</item>
- <item msgid="2715700596495505626">"256 КБ"</item>
- <item msgid="7099386891713159947">"1 МБ"</item>
- <item msgid="6069075827077845520">"4 МБ"</item>
- <item msgid="8243549501764402572">"16 МБ"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Өшірулі"</item>
<item msgid="4064786181089783077">"64 КБ"</item>
<item msgid="3052710745383602630">"256 КБ"</item>
<item msgid="3691785423374588514">"1 МБ"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Өшірулі"</item>
- <item msgid="4195153527464162486">"Әр журнал буферіне 64 КБ"</item>
- <item msgid="7464037639415220106">"Әр журнал буферіне 256 КБ"</item>
- <item msgid="8539423820514360724">"Әр журнал буферіне 1 МБ"</item>
- <item msgid="1984761927103140651">"Әр журнал буферіне 4 МБ"</item>
- <item msgid="7892098981256010498">"Әр журнал буферіне 16 МБ"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Өшірулі"</item>
<item msgid="6014837961827347618">"Барлығы"</item>
diff --git a/packages/SettingsLib/res/values-km/arrays.xml b/packages/SettingsLib/res/values-km/arrays.xml
index 6f4589e..70c1e33 100644
--- a/packages/SettingsLib/res/values-km/arrays.xml
+++ b/packages/SettingsLib/res/values-km/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">"សកម្ម (មេឌៀ)"</item>
<item msgid="5001852592115448348">"សកម្ម (ទូរសព្ទ)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"បិទ"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"បិទ"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"បិទ"</item>
- <item msgid="4195153527464162486">"64K per log buffer"</item>
- <item msgid="7464037639415220106">"256K per log buffer"</item>
- <item msgid="8539423820514360724">"1M per log buffer"</item>
- <item msgid="1984761927103140651">"4M per log buffer"</item>
- <item msgid="7892098981256010498">"16M per log buffer"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"បិទ"</item>
<item msgid="6014837961827347618">"ទាំងអស់"</item>
diff --git a/packages/SettingsLib/res/values-kn/arrays.xml b/packages/SettingsLib/res/values-kn/arrays.xml
index 7e543dd..1bfcdc0 100644
--- a/packages/SettingsLib/res/values-kn/arrays.xml
+++ b/packages/SettingsLib/res/values-kn/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", ಸಕ್ರಿಯ (ಮಾಧ್ಯಮ)"</item>
<item msgid="5001852592115448348">", ಸಕ್ರಿಯ (ಫೋನ್)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"ಆಫ್"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"ಆಫ್"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"ಆಫ್"</item>
- <item msgid="4195153527464162486">"ಪ್ರತಿ ಲಾಗ್ ಬಫರ್ಗೆ 64K"</item>
- <item msgid="7464037639415220106">"ಪ್ರತಿ ಲಾಗ್ ಬಫರ್ಗೆ 256K"</item>
- <item msgid="8539423820514360724">"ಪ್ರತಿ ಲಾಗ್ ಬಫರ್ಗೆ 1M"</item>
- <item msgid="1984761927103140651">"ಪ್ರತಿ ಲಾಗ್ ಬಫರ್ಗೆ 4M"</item>
- <item msgid="7892098981256010498">"ಪ್ರತಿ ಲಾಗ್ ಬಫರ್ಗೆ 16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"ಆಫ್"</item>
<item msgid="6014837961827347618">"ಎಲ್ಲಾ"</item>
diff --git a/packages/SettingsLib/res/values-ko/arrays.xml b/packages/SettingsLib/res/values-ko/arrays.xml
index 9a18c16..648188f 100644
--- a/packages/SettingsLib/res/values-ko/arrays.xml
+++ b/packages/SettingsLib/res/values-ko/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", 활성(미디어)"</item>
<item msgid="5001852592115448348">", 활성(휴대전화)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"사용 안함"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"사용 안함"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"사용 안함"</item>
- <item msgid="4195153527464162486">"로그 버퍼당 64K"</item>
- <item msgid="7464037639415220106">"로그 버퍼당 256K"</item>
- <item msgid="8539423820514360724">"로그 버퍼당 1M"</item>
- <item msgid="1984761927103140651">"로그 버퍼당 4M"</item>
- <item msgid="7892098981256010498">"로그 버퍼당 16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"사용 안함"</item>
<item msgid="6014837961827347618">"전체"</item>
diff --git a/packages/SettingsLib/res/values-ky/arrays.xml b/packages/SettingsLib/res/values-ky/arrays.xml
index f5e812d..295c174 100644
--- a/packages/SettingsLib/res/values-ky/arrays.xml
+++ b/packages/SettingsLib/res/values-ky/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", жандырылган (аудио)"</item>
<item msgid="5001852592115448348">", жандырылган (телефон)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Өчүк"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Өчүк"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Өчүк"</item>
- <item msgid="4195153527464162486">"Буфер: 64КБ ашпашы керек"</item>
- <item msgid="7464037639415220106">"Буфер: 256КБ ашпашы керек"</item>
- <item msgid="8539423820514360724">"Буфер: 1М ашпашы керек"</item>
- <item msgid="1984761927103140651">"Буфер: 4М ашпашы керек"</item>
- <item msgid="7892098981256010498">"Буфер: 16М ашпашы керек"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Өчүк"</item>
<item msgid="6014837961827347618">"Бардыгы"</item>
diff --git a/packages/SettingsLib/res/values-lo/arrays.xml b/packages/SettingsLib/res/values-lo/arrays.xml
index a0fb2b8..c48eb3b 100644
--- a/packages/SettingsLib/res/values-lo/arrays.xml
+++ b/packages/SettingsLib/res/values-lo/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", ອອນລາຍ (ມີເດຍ)"</item>
<item msgid="5001852592115448348">", ອອນລາຍ (ໂທລະສັບ)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"ປິດ"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"ປິດ"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"ປິດ"</item>
- <item msgid="4195153527464162486">"ບັບເຟີ 64K ຕໍ່ບັນທຶກ"</item>
- <item msgid="7464037639415220106">"ບັບເຟີ 256K ຕໍ່ບັນທຶກ"</item>
- <item msgid="8539423820514360724">"ບັບເຟີ 1M ຕໍ່ບັນທຶກ"</item>
- <item msgid="1984761927103140651">"ບັບເຟີ 4M ຕໍ່ບັນທຶກ"</item>
- <item msgid="7892098981256010498">"ບັບເຟີ 16M ຕໍ່ບັນທຶກ"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"ປິດ"</item>
<item msgid="6014837961827347618">"ທັງໝົດ"</item>
diff --git a/packages/SettingsLib/res/values-lt/arrays.xml b/packages/SettingsLib/res/values-lt/arrays.xml
index 90a77bf..48b69c8 100644
--- a/packages/SettingsLib/res/values-lt/arrays.xml
+++ b/packages/SettingsLib/res/values-lt/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", aktyvus (medija)"</item>
<item msgid="5001852592115448348">", aktyvus (telefonas)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Išjungta"</item>
- <item msgid="7839165897132179888">"64 KB"</item>
- <item msgid="2715700596495505626">"256 KB"</item>
- <item msgid="7099386891713159947">"1 MB"</item>
- <item msgid="6069075827077845520">"4 MB"</item>
- <item msgid="8243549501764402572">"16 MB"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Išjungta"</item>
<item msgid="4064786181089783077">"64 KB"</item>
<item msgid="3052710745383602630">"256 KB"</item>
<item msgid="3691785423374588514">"1 MB"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Išjungta"</item>
- <item msgid="4195153527464162486">"64 KB žurnalo buferis"</item>
- <item msgid="7464037639415220106">"256 KB žurnalo buferis"</item>
- <item msgid="8539423820514360724">"1 MB žurnalo buferis"</item>
- <item msgid="1984761927103140651">"4 MB žurnalo buferis"</item>
- <item msgid="7892098981256010498">"16 MB žurnalo buferis"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Išjungta"</item>
<item msgid="6014837961827347618">"Viskas"</item>
diff --git a/packages/SettingsLib/res/values-lv/arrays.xml b/packages/SettingsLib/res/values-lv/arrays.xml
index 5891727..81a3721 100644
--- a/packages/SettingsLib/res/values-lv/arrays.xml
+++ b/packages/SettingsLib/res/values-lv/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", aktīva (multivide)"</item>
<item msgid="5001852592115448348">", aktīva (tālrunis)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Izslēgts"</item>
- <item msgid="7839165897132179888">"64 KB"</item>
- <item msgid="2715700596495505626">"256 KB"</item>
- <item msgid="7099386891713159947">"1 MB"</item>
- <item msgid="6069075827077845520">"4 MB"</item>
- <item msgid="8243549501764402572">"16 MB"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Izslēgts"</item>
<item msgid="4064786181089783077">"64 KB"</item>
<item msgid="3052710745383602630">"256 KB"</item>
<item msgid="3691785423374588514">"1 MB"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Izslēgts"</item>
- <item msgid="4195153527464162486">"64 KB vienam žurnāla buferim"</item>
- <item msgid="7464037639415220106">"256 KB vienam žurnāla buferim"</item>
- <item msgid="8539423820514360724">"1 MB vienam žurnāla buferim"</item>
- <item msgid="1984761927103140651">"4 MB vienam žurnāla buferim"</item>
- <item msgid="7892098981256010498">"16 MB vienam žurnāla buferim"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Izslēgts"</item>
<item msgid="6014837961827347618">"Visi"</item>
diff --git a/packages/SettingsLib/res/values-mk/arrays.xml b/packages/SettingsLib/res/values-mk/arrays.xml
index 388e280..90a97c7 100644
--- a/packages/SettingsLib/res/values-mk/arrays.xml
+++ b/packages/SettingsLib/res/values-mk/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", активен (аудиосодржини)"</item>
<item msgid="5001852592115448348">", активен (телефон)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Исклучено"</item>
- <item msgid="7839165897132179888">"64.000"</item>
- <item msgid="2715700596495505626">"256.000"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Исклучено"</item>
<item msgid="4064786181089783077">"64.000"</item>
<item msgid="3052710745383602630">"256.000"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Исклучено"</item>
- <item msgid="4195153527464162486">"64 K/меѓумеморија"</item>
- <item msgid="7464037639415220106">"256 K/меѓумеморија"</item>
- <item msgid="8539423820514360724">"1 M/меѓумеморија"</item>
- <item msgid="1984761927103140651">"4 M/меѓумеморија"</item>
- <item msgid="7892098981256010498">"16 M/меѓумеморија"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Исклучено"</item>
<item msgid="6014837961827347618">"Сите"</item>
diff --git a/packages/SettingsLib/res/values-ml/arrays.xml b/packages/SettingsLib/res/values-ml/arrays.xml
index cb31d22..5ea0615 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 485a228..1455669 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 6a33b48..e58ff66 100644
--- a/packages/SettingsLib/res/values-mn/arrays.xml
+++ b/packages/SettingsLib/res/values-mn/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", идэвхтэй (медиа)"</item>
<item msgid="5001852592115448348">", идэвхтэй (утас)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Идэвхгүй"</item>
- <item msgid="7839165897132179888">"64000"</item>
- <item msgid="2715700596495505626">"256000"</item>
- <item msgid="7099386891713159947">"1 сая"</item>
- <item msgid="6069075827077845520">"4 сая"</item>
- <item msgid="8243549501764402572">"16 сая"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Идэвхгүй"</item>
<item msgid="4064786181089783077">"64000"</item>
<item msgid="3052710745383602630">"256000"</item>
<item msgid="3691785423374588514">"1 сая"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Идэвхгүй"</item>
- <item msgid="4195153527464162486">"лог буфер бүрд 64K"</item>
- <item msgid="7464037639415220106">"лог буфер бүрд 256K"</item>
- <item msgid="8539423820514360724">"лог буфер бүрд 1M"</item>
- <item msgid="1984761927103140651">"лог буфер бүрд 4M"</item>
- <item msgid="7892098981256010498">"лог буфер бүрд 16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Идэвхгүй"</item>
<item msgid="6014837961827347618">"Бүгд"</item>
diff --git a/packages/SettingsLib/res/values-mr/arrays.xml b/packages/SettingsLib/res/values-mr/arrays.xml
index 8abf290..aaf51b3 100644
--- a/packages/SettingsLib/res/values-mr/arrays.xml
+++ b/packages/SettingsLib/res/values-mr/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", अॅक्टिव्ह (मीडिया)"</item>
<item msgid="5001852592115448348">", अॅक्टिव्ह (फोन)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"बंद"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"बंद"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"बंद"</item>
- <item msgid="4195153527464162486">"प्रति लॉग बफर 64K"</item>
- <item msgid="7464037639415220106">"प्रति लॉग बफर 256K"</item>
- <item msgid="8539423820514360724">"प्रति लॉग बफर 1M"</item>
- <item msgid="1984761927103140651">"प्रति लॉग बफर 4M"</item>
- <item msgid="7892098981256010498">"प्रति लॉग बफर 16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"बंद"</item>
<item msgid="6014837961827347618">"सर्व"</item>
diff --git a/packages/SettingsLib/res/values-ms/arrays.xml b/packages/SettingsLib/res/values-ms/arrays.xml
index 15fad67..d2fc10e 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 1ba076d..9527793 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 90bac81..3c69335 100644
--- a/packages/SettingsLib/res/values-my/arrays.xml
+++ b/packages/SettingsLib/res/values-my/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">"၊ ဖွင့်ထားသည် (မီဒီယာ)"</item>
<item msgid="5001852592115448348">"၊ ဖွင့်ထားသည် (ဖုန်း)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"ပိတ်ရန်"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"ပိတ်ရန်"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"ပိတ်ရန်"</item>
- <item msgid="4195153527464162486">"မှတ်တမ်းယာယီကြားခံနယ်တစ်ခုလျှင် 64K"</item>
- <item msgid="7464037639415220106">"မှတ်တမ်းယာယီကြားခံနယ်တစ်ခုလျှင် 256K"</item>
- <item msgid="8539423820514360724">"မှတ်တမ်းကြားခံနယ် တစ်ခုလျှင် 1M"</item>
- <item msgid="1984761927103140651">"မှတ်တမ်းယာယီကြားခံနယ်တစ်ခုလျှင် 4M"</item>
- <item msgid="7892098981256010498">"မှတ်တမ်းယာယီကြားခံနယ်တစ်ခုလျှင် 16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"ပိတ်ရန်"</item>
<item msgid="6014837961827347618">"အားလုံး"</item>
diff --git a/packages/SettingsLib/res/values-nb/arrays.xml b/packages/SettingsLib/res/values-nb/arrays.xml
index 275018b..5e6ee65 100644
--- a/packages/SettingsLib/res/values-nb/arrays.xml
+++ b/packages/SettingsLib/res/values-nb/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", aktiv (media)"</item>
<item msgid="5001852592115448348">", aktiv (telefon)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Av"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Av"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Av"</item>
- <item msgid="4195153527464162486">"64K per loggbuffer"</item>
- <item msgid="7464037639415220106">"256K per loggbuffer"</item>
- <item msgid="8539423820514360724">"1M per loggbuffer"</item>
- <item msgid="1984761927103140651">"4M per loggbuffer"</item>
- <item msgid="7892098981256010498">"16M per loggbuffer"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Av"</item>
<item msgid="6014837961827347618">"Alle"</item>
diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml
index 5ee6353..c8b89c7 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 c0e9fe5..5795cc9 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 bfbbae0..a20db9d 100644
--- a/packages/SettingsLib/res/values-nl/arrays.xml
+++ b/packages/SettingsLib/res/values-nl/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", actief (media)"</item>
<item msgid="5001852592115448348">", actief (telefoon)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Uit"</item>
- <item msgid="7839165897132179888">"64 K"</item>
- <item msgid="2715700596495505626">"256 K"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Uit"</item>
<item msgid="4064786181089783077">"64 K"</item>
<item msgid="3052710745383602630">"256 K"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Uit"</item>
- <item msgid="4195153527464162486">"64 K per logbuffer"</item>
- <item msgid="7464037639415220106">"256 K per logbuffer"</item>
- <item msgid="8539423820514360724">"1 M per logbuffer"</item>
- <item msgid="1984761927103140651">"4 M per logbuffer"</item>
- <item msgid="7892098981256010498">"16 M per logbuffer"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Uit"</item>
<item msgid="6014837961827347618">"Alle"</item>
diff --git a/packages/SettingsLib/res/values-or/arrays.xml b/packages/SettingsLib/res/values-or/arrays.xml
index 4a3d5d7..b7de25e 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 ac1ccb6..41e84f9 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 d594f3b..c64ee76 100644
--- a/packages/SettingsLib/res/values-pa/arrays.xml
+++ b/packages/SettingsLib/res/values-pa/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", ਕਿਰਿਆਸ਼ੀਲ (ਮੀਡੀਆ)"</item>
<item msgid="5001852592115448348">", ਕਿਰਿਆਸ਼ੀਲ (ਫ਼ੋਨ)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"ਬੰਦ"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"ਬੰਦ"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"ਬੰਦ"</item>
- <item msgid="4195153527464162486">"64K ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item>
- <item msgid="7464037639415220106">"256K ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item>
- <item msgid="8539423820514360724">"1M ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item>
- <item msgid="1984761927103140651">"4M ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item>
- <item msgid="7892098981256010498">"16M ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"ਬੰਦ"</item>
<item msgid="6014837961827347618">"ਸਭ"</item>
diff --git a/packages/SettingsLib/res/values-pl/arrays.xml b/packages/SettingsLib/res/values-pl/arrays.xml
index eb33323..e873b7e 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 cd6f25c..a57e541 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 81285aa..c0dcec0 100644
--- a/packages/SettingsLib/res/values-pt-rBR/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", ativo (mídia)"</item>
<item msgid="5001852592115448348">", ativo (telefone)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Desativado"</item>
- <item msgid="7839165897132179888">"64 K"</item>
- <item msgid="2715700596495505626">"256 K"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Desativado"</item>
<item msgid="4064786181089783077">"64 K"</item>
<item msgid="3052710745383602630">"256 K"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Desativado"</item>
- <item msgid="4195153527464162486">"64 K/buffer de registro"</item>
- <item msgid="7464037639415220106">"256 K/buffer de registro"</item>
- <item msgid="8539423820514360724">"1 M/buffer de registro"</item>
- <item msgid="1984761927103140651">"4 M/buffer de registro"</item>
- <item msgid="7892098981256010498">"16 M/buffer de registro"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Desativado"</item>
<item msgid="6014837961827347618">"Todos"</item>
diff --git a/packages/SettingsLib/res/values-pt-rPT/arrays.xml b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
index 019a5f6..de63257 100644
--- a/packages/SettingsLib/res/values-pt-rPT/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", ativo (multimédia)"</item>
<item msgid="5001852592115448348">", ativo (telemóvel)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Desativado"</item>
- <item msgid="7839165897132179888">"64 K"</item>
- <item msgid="2715700596495505626">"256 K"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Desativado"</item>
<item msgid="4064786181089783077">"64 K"</item>
<item msgid="3052710745383602630">"256 K"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Desativado"</item>
- <item msgid="4195153527464162486">"64 K por buffer de registo"</item>
- <item msgid="7464037639415220106">"256 K por buffer de registo"</item>
- <item msgid="8539423820514360724">"1 M por buffer de registo"</item>
- <item msgid="1984761927103140651">"4 M por buffer de registo"</item>
- <item msgid="7892098981256010498">"16 M por buffer de registo"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Desativado"</item>
<item msgid="6014837961827347618">"Todos"</item>
diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml
index 81285aa..c0dcec0 100644
--- a/packages/SettingsLib/res/values-pt/arrays.xml
+++ b/packages/SettingsLib/res/values-pt/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", ativo (mídia)"</item>
<item msgid="5001852592115448348">", ativo (telefone)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Desativado"</item>
- <item msgid="7839165897132179888">"64 K"</item>
- <item msgid="2715700596495505626">"256 K"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Desativado"</item>
<item msgid="4064786181089783077">"64 K"</item>
<item msgid="3052710745383602630">"256 K"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Desativado"</item>
- <item msgid="4195153527464162486">"64 K/buffer de registro"</item>
- <item msgid="7464037639415220106">"256 K/buffer de registro"</item>
- <item msgid="8539423820514360724">"1 M/buffer de registro"</item>
- <item msgid="1984761927103140651">"4 M/buffer de registro"</item>
- <item msgid="7892098981256010498">"16 M/buffer de registro"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Desativado"</item>
<item msgid="6014837961827347618">"Todos"</item>
diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml
index 48a3fa7..0fe0ef0 100644
--- a/packages/SettingsLib/res/values-ro/arrays.xml
+++ b/packages/SettingsLib/res/values-ro/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", activ (media)"</item>
<item msgid="5001852592115448348">", activ (telefon)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Dezactivată"</item>
- <item msgid="7839165897132179888">"64 KB"</item>
- <item msgid="2715700596495505626">"256 KB"</item>
- <item msgid="7099386891713159947">"1 MB"</item>
- <item msgid="6069075827077845520">"4 MB"</item>
- <item msgid="8243549501764402572">"16 MB"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Dezactivată"</item>
<item msgid="4064786181089783077">"64 KB"</item>
<item msgid="3052710745383602630">"256 KB"</item>
<item msgid="3691785423374588514">"1 MB"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Dezactivată"</item>
- <item msgid="4195153527464162486">"64 KB/mem. temporară de înregistrări în jurnal"</item>
- <item msgid="7464037639415220106">"256 KB/mem. temporară de înregistrări în jurnal"</item>
- <item msgid="8539423820514360724">"1 MB/mem. temporară de înregistrări în jurnal"</item>
- <item msgid="1984761927103140651">"4 MB/mem. temporară de înregistrări în jurnal"</item>
- <item msgid="7892098981256010498">"16 MB/mem. temporară de înregistrări în jurnal"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Dezactivată"</item>
<item msgid="6014837961827347618">"Toate"</item>
diff --git a/packages/SettingsLib/res/values-ru/arrays.xml b/packages/SettingsLib/res/values-ru/arrays.xml
index 5617aa6..84c3dc6 100644
--- a/packages/SettingsLib/res/values-ru/arrays.xml
+++ b/packages/SettingsLib/res/values-ru/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", активно (A2DP)"</item>
<item msgid="5001852592115448348">", активно (HSP/HFP)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Отключено"</item>
- <item msgid="7839165897132179888">"64 КБ"</item>
- <item msgid="2715700596495505626">"256 КБ"</item>
- <item msgid="7099386891713159947">"1 МБ"</item>
- <item msgid="6069075827077845520">"4 МБ"</item>
- <item msgid="8243549501764402572">"16 МБ"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Отключено"</item>
<item msgid="4064786181089783077">"64 КБ"</item>
<item msgid="3052710745383602630">"256 КБ"</item>
<item msgid="3691785423374588514">"1 МБ"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Отключено"</item>
- <item msgid="4195153527464162486">"Буфер: макс. 64 КБ"</item>
- <item msgid="7464037639415220106">"Буфер: макс. 256 КБ"</item>
- <item msgid="8539423820514360724">"Буфер: макс. 1 МБ"</item>
- <item msgid="1984761927103140651">"Буфер: макс. 4 МБ"</item>
- <item msgid="7892098981256010498">"Буфер: макс. 16 МБ"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Отключено"</item>
<item msgid="6014837961827347618">"Все"</item>
diff --git a/packages/SettingsLib/res/values-si/arrays.xml b/packages/SettingsLib/res/values-si/arrays.xml
index 01d0dd2..81d0bbb 100644
--- a/packages/SettingsLib/res/values-si/arrays.xml
+++ b/packages/SettingsLib/res/values-si/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", ක්රියාකාරී (මාධ්ය)"</item>
<item msgid="5001852592115448348">", ක්රියාකාරී (දුරකථන)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"ක්රියාවිරහිතය"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"ක්රියාවිරහිතය"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"ක්රියාවිරහිතය"</item>
- <item msgid="4195153527464162486">"ලොග අන්තරාවකට 64K"</item>
- <item msgid="7464037639415220106">"ලොග අන්තරාවකට 256K"</item>
- <item msgid="8539423820514360724">"ලොග අන්තරාවකට 1M"</item>
- <item msgid="1984761927103140651">"ලොග අන්තරාවකට 4M"</item>
- <item msgid="7892098981256010498">"ලොග අන්තරාවකට 16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"ක්රියාවිරහිතය"</item>
<item msgid="6014837961827347618">"සියලු"</item>
diff --git a/packages/SettingsLib/res/values-sk/arrays.xml b/packages/SettingsLib/res/values-sk/arrays.xml
index 5dcf791..2826cb3 100644
--- a/packages/SettingsLib/res/values-sk/arrays.xml
+++ b/packages/SettingsLib/res/values-sk/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", aktívne (médiá)"</item>
<item msgid="5001852592115448348">", aktívne (telefón)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Vypnuté"</item>
- <item msgid="7839165897132179888">"64 kB"</item>
- <item msgid="2715700596495505626">"256 kB"</item>
- <item msgid="7099386891713159947">"1 MB"</item>
- <item msgid="6069075827077845520">"4 MB"</item>
- <item msgid="8243549501764402572">"16 MB"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Vypnuté"</item>
<item msgid="4064786181089783077">"64 kB"</item>
<item msgid="3052710745383602630">"256 kB"</item>
<item msgid="3691785423374588514">"1 MB"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Vypnuté"</item>
- <item msgid="4195153527464162486">"64 kB na vyrov. pamäť denníka"</item>
- <item msgid="7464037639415220106">"256 kB na vyrov. pamäť denníka"</item>
- <item msgid="8539423820514360724">"1 MB na vyrov. pam. denníka"</item>
- <item msgid="1984761927103140651">"4 MB na vyrov. pamäť denníka"</item>
- <item msgid="7892098981256010498">"16 MB na vyrov. pamäť denníka"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Vypnuté"</item>
<item msgid="6014837961827347618">"Všetko"</item>
diff --git a/packages/SettingsLib/res/values-sl/arrays.xml b/packages/SettingsLib/res/values-sl/arrays.xml
index 7ba23af..26c6e6e 100644
--- a/packages/SettingsLib/res/values-sl/arrays.xml
+++ b/packages/SettingsLib/res/values-sl/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", aktivno (predstavnost)"</item>
<item msgid="5001852592115448348">", aktivno (telefon)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Izklopljeno"</item>
- <item msgid="7839165897132179888">"64 K"</item>
- <item msgid="2715700596495505626">"256 K"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Izklopljeno"</item>
<item msgid="4064786181089783077">"64 K"</item>
<item msgid="3052710745383602630">"256 K"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Izklopljeno"</item>
- <item msgid="4195153527464162486">"64 K/medpomnilnik dnevnika"</item>
- <item msgid="7464037639415220106">"256 K/medpomnilnik dnevnika"</item>
- <item msgid="8539423820514360724">"1 M/medpomnilnik dnevnika"</item>
- <item msgid="1984761927103140651">"4 M/medpomnilnik dnevnika"</item>
- <item msgid="7892098981256010498">"16 M/medpomnilnik dnevnika"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Izklopljeno"</item>
<item msgid="6014837961827347618">"Vse"</item>
diff --git a/packages/SettingsLib/res/values-sq/arrays.xml b/packages/SettingsLib/res/values-sq/arrays.xml
index fccfc2f..3db2e6b 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 b4e5db9..d5c0231 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 11b4b76..ec4da5a 100644
--- a/packages/SettingsLib/res/values-sr/arrays.xml
+++ b/packages/SettingsLib/res/values-sr/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", активан (медијски)"</item>
<item msgid="5001852592115448348">", активан (телефон)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Искључено"</item>
- <item msgid="7839165897132179888">"64 kB"</item>
- <item msgid="2715700596495505626">"256 kB"</item>
- <item msgid="7099386891713159947">"1 MB"</item>
- <item msgid="6069075827077845520">"4 MB"</item>
- <item msgid="8243549501764402572">"16 MB"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Искључено"</item>
<item msgid="4064786181089783077">"64 kB"</item>
<item msgid="3052710745383602630">"256 kB"</item>
<item msgid="3691785423374588514">"1 MB"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Искључено"</item>
- <item msgid="4195153527464162486">"64 kB по међумеморији евиденције"</item>
- <item msgid="7464037639415220106">"256 kB по међумеморији евиденције"</item>
- <item msgid="8539423820514360724">"1 MB по међумеморији евиденције"</item>
- <item msgid="1984761927103140651">"4 MB по међумеморији евиденције"</item>
- <item msgid="7892098981256010498">"16 MB по међумеморији евиденције"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Искључено"</item>
<item msgid="6014837961827347618">"Све"</item>
diff --git a/packages/SettingsLib/res/values-sv/arrays.xml b/packages/SettingsLib/res/values-sv/arrays.xml
index be68c71..b631f44 100644
--- a/packages/SettingsLib/res/values-sv/arrays.xml
+++ b/packages/SettingsLib/res/values-sv/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", aktiv (media)"</item>
<item msgid="5001852592115448348">", aktiv (telefon)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Av"</item>
- <item msgid="7839165897132179888">"64 kB"</item>
- <item msgid="2715700596495505626">"256 kB"</item>
- <item msgid="7099386891713159947">"1 MB"</item>
- <item msgid="6069075827077845520">"4 MB"</item>
- <item msgid="8243549501764402572">"16 MB"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Av"</item>
<item msgid="4064786181089783077">"64 kB"</item>
<item msgid="3052710745383602630">"256 kB"</item>
<item msgid="3691785423374588514">"1 MB"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Av"</item>
- <item msgid="4195153527464162486">"64 kB/loggbuffert"</item>
- <item msgid="7464037639415220106">"256 kB/loggbuffert"</item>
- <item msgid="8539423820514360724">"1 MB/loggbuffert"</item>
- <item msgid="1984761927103140651">"4 MB/loggbuffert"</item>
- <item msgid="7892098981256010498">"16 MB/loggbuffert"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Av"</item>
<item msgid="6014837961827347618">"Alla"</item>
diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml
index da99b91..cd15e2c 100644
--- a/packages/SettingsLib/res/values-sw/arrays.xml
+++ b/packages/SettingsLib/res/values-sw/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", inatumika (maudhui)"</item>
<item msgid="5001852592115448348">", inatumika (simu)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Imezimwa"</item>
- <item msgid="7839165897132179888">"K64"</item>
- <item msgid="2715700596495505626">"K256"</item>
- <item msgid="7099386891713159947">"M1"</item>
- <item msgid="6069075827077845520">"M4"</item>
- <item msgid="8243549501764402572">"M16"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Imezimwa"</item>
<item msgid="4064786181089783077">"K64"</item>
<item msgid="3052710745383602630">"K256"</item>
<item msgid="3691785423374588514">"M1"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Imezimwa"</item>
- <item msgid="4195153527464162486">"K64 kwa kila akiba ya kumbukumbu"</item>
- <item msgid="7464037639415220106">"K256 kwa kila akiba ya kumbukumbu"</item>
- <item msgid="8539423820514360724">"M1 kwa kila akiba ya kumbukumbu"</item>
- <item msgid="1984761927103140651">"M4 kwa kila akiba ya kumbukumbu"</item>
- <item msgid="7892098981256010498">"M16 kwa kila akiba ya kumbukumbu"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Yamezimwa"</item>
<item msgid="6014837961827347618">"Zote"</item>
diff --git a/packages/SettingsLib/res/values-ta/arrays.xml b/packages/SettingsLib/res/values-ta/arrays.xml
index 1c55954..01b0a8e 100644
--- a/packages/SettingsLib/res/values-ta/arrays.xml
+++ b/packages/SettingsLib/res/values-ta/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", செயலில் உள்ளது (மீடியா)"</item>
<item msgid="5001852592115448348">", செயலில் உள்ளது (மொபைல்)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"ஆஃப்"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"ஆஃப்"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"ஆஃப்"</item>
- <item msgid="4195153527464162486">"64K / லாக் பஃபர்"</item>
- <item msgid="7464037639415220106">"256K / லாக் பஃபர்"</item>
- <item msgid="8539423820514360724">"1M / லாக் பஃபர்"</item>
- <item msgid="1984761927103140651">"4M / லாக் பஃபர்"</item>
- <item msgid="7892098981256010498">"16M / லாக் பஃபர்"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"ஆஃப்"</item>
<item msgid="6014837961827347618">"எல்லாம்"</item>
diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml
index e1c0406..4bdd55b 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 33f082d..897abea 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"మొబైల్ డేటా ఆఫ్లో ఉంది"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"డేటాను ఉపయోగించే విధంగా సెట్ చేయలేదు"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"ఫోన్ లేదు."</string>
diff --git a/packages/SettingsLib/res/values-th/arrays.xml b/packages/SettingsLib/res/values-th/arrays.xml
index 21fe6e4..9f6080b 100644
--- a/packages/SettingsLib/res/values-th/arrays.xml
+++ b/packages/SettingsLib/res/values-th/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">"ใช้งานอยู่ (สื่อ)"</item>
<item msgid="5001852592115448348">"ใช้งานอยู่ (โทรศัพท์)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"ปิด"</item>
- <item msgid="7839165897132179888">"64 K"</item>
- <item msgid="2715700596495505626">"256 K"</item>
- <item msgid="7099386891713159947">"1 M"</item>
- <item msgid="6069075827077845520">"4 M"</item>
- <item msgid="8243549501764402572">"16 M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"ปิด"</item>
<item msgid="4064786181089783077">"64 K"</item>
<item msgid="3052710745383602630">"256 K"</item>
<item msgid="3691785423374588514">"1 M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"ปิด"</item>
- <item msgid="4195153527464162486">"64 K ต่อบัฟเฟอร์ไฟล์บันทึก"</item>
- <item msgid="7464037639415220106">"256 K ต่อบัฟเฟอร์ไฟล์บันทึก"</item>
- <item msgid="8539423820514360724">"1 M ต่อบัฟเฟอร์ไฟล์บันทึก"</item>
- <item msgid="1984761927103140651">"4 M ต่อบัฟเฟอร์ไฟล์บันทึก"</item>
- <item msgid="7892098981256010498">"16 M ต่อบัฟเฟอร์ไฟล์บันทึก"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"ปิด"</item>
<item msgid="6014837961827347618">"ทั้งหมด"</item>
diff --git a/packages/SettingsLib/res/values-tl/arrays.xml b/packages/SettingsLib/res/values-tl/arrays.xml
index 9f07cff..ab68a68 100644
--- a/packages/SettingsLib/res/values-tl/arrays.xml
+++ b/packages/SettingsLib/res/values-tl/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", aktibo (media)"</item>
<item msgid="5001852592115448348">", aktibo (telepono)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"I-off"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"I-off"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"I-off"</item>
- <item msgid="4195153527464162486">"64K kada log buffer"</item>
- <item msgid="7464037639415220106">"256K kada log buffer"</item>
- <item msgid="8539423820514360724">"1M kada log buffer"</item>
- <item msgid="1984761927103140651">"4M kada log buffer"</item>
- <item msgid="7892098981256010498">"16M kada log buffer"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Naka-off"</item>
<item msgid="6014837961827347618">"Lahat"</item>
diff --git a/packages/SettingsLib/res/values-tr/arrays.xml b/packages/SettingsLib/res/values-tr/arrays.xml
index fc90c9a..d5d578c 100644
--- a/packages/SettingsLib/res/values-tr/arrays.xml
+++ b/packages/SettingsLib/res/values-tr/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", etkin (medya)"</item>
<item msgid="5001852592115448348">", etkin (telefon)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Kapalı"</item>
- <item msgid="7839165897132179888">"64 KB"</item>
- <item msgid="2715700596495505626">"256 KB"</item>
- <item msgid="7099386891713159947">"1 MB"</item>
- <item msgid="6069075827077845520">"4 MB"</item>
- <item msgid="8243549501764402572">"16 MB"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Kapalı"</item>
<item msgid="4064786181089783077">"64 KB"</item>
<item msgid="3052710745383602630">"256 KB"</item>
<item msgid="3691785423374588514">"1 MB"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Kapalı"</item>
- <item msgid="4195153527464162486">"Günlük arabelleği başına 64 KB"</item>
- <item msgid="7464037639415220106">"Günlük arabelleği başına 256 KB"</item>
- <item msgid="8539423820514360724">"Günlük arabelleği başına 1 MB"</item>
- <item msgid="1984761927103140651">"Günlük arabelleği başına 4 MB"</item>
- <item msgid="7892098981256010498">"Günlük arabelleği başına 16 MB"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Kapalı"</item>
<item msgid="6014837961827347618">"Tümü"</item>
diff --git a/packages/SettingsLib/res/values-uk/arrays.xml b/packages/SettingsLib/res/values-uk/arrays.xml
index 4405c37..41922a3 100644
--- a/packages/SettingsLib/res/values-uk/arrays.xml
+++ b/packages/SettingsLib/res/values-uk/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", активний (лише для медіа)"</item>
<item msgid="5001852592115448348">", активний (лише для телефона)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Вимкнено"</item>
- <item msgid="7839165897132179888">"64 КБ"</item>
- <item msgid="2715700596495505626">"256 КБ"</item>
- <item msgid="7099386891713159947">"1 МБ"</item>
- <item msgid="6069075827077845520">"4 МБ"</item>
- <item msgid="8243549501764402572">"16 МБ"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Вимкнено"</item>
<item msgid="4064786181089783077">"64 КБ"</item>
<item msgid="3052710745383602630">"256 КБ"</item>
<item msgid="3691785423374588514">"1 МБ"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Вимкнено"</item>
- <item msgid="4195153527464162486">"Буфер журналу: 64 КБ"</item>
- <item msgid="7464037639415220106">"Буфер журналу: 256 КБ"</item>
- <item msgid="8539423820514360724">"Буфер журналу: 1 МБ"</item>
- <item msgid="1984761927103140651">"Буфер журналу: 4 МБ"</item>
- <item msgid="7892098981256010498">"Буфер журналу: 16 МБ"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Вимкнено"</item>
<item msgid="6014837961827347618">"Усі"</item>
diff --git a/packages/SettingsLib/res/values-ur/arrays.xml b/packages/SettingsLib/res/values-ur/arrays.xml
index f4c2500..a3539ff 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 9376bdc..82783b3 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 0770dcc..e695e20 100644
--- a/packages/SettingsLib/res/values-uz/arrays.xml
+++ b/packages/SettingsLib/res/values-uz/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", faol (media)"</item>
<item msgid="5001852592115448348">", faol (telefon)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Yoqilmagan"</item>
- <item msgid="7839165897132179888">"64 KB"</item>
- <item msgid="2715700596495505626">"256 KB"</item>
- <item msgid="7099386891713159947">"1 MB"</item>
- <item msgid="6069075827077845520">"4 MB"</item>
- <item msgid="8243549501764402572">"16 MB"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Yoqilmagan"</item>
<item msgid="4064786181089783077">"64 KB"</item>
<item msgid="3052710745383602630">"256 KB"</item>
<item msgid="3691785423374588514">"1 MB"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Yoqilmagan"</item>
- <item msgid="4195153527464162486">"Bufer: maks. 64 KB"</item>
- <item msgid="7464037639415220106">"Bufer: maks. 256 KB"</item>
- <item msgid="8539423820514360724">"Bufer: maks. 1 MB"</item>
- <item msgid="1984761927103140651">"Bufer: maks. 4 MB"</item>
- <item msgid="7892098981256010498">"Bufer: maks. 16 MB"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Yoqilmagan"</item>
<item msgid="6014837961827347618">"Hammasi"</item>
diff --git a/packages/SettingsLib/res/values-vi/arrays.xml b/packages/SettingsLib/res/values-vi/arrays.xml
index 635cf11..cac6c46 100644
--- a/packages/SettingsLib/res/values-vi/arrays.xml
+++ b/packages/SettingsLib/res/values-vi/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", đang hoạt động (nội dung nghe nhìn)"</item>
<item msgid="5001852592115448348">", đang hoạt động (điện thoại)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Tắt"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Tắt"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Tắt"</item>
- <item msgid="4195153527464162486">"64K mỗi bộ đệm nhật ký"</item>
- <item msgid="7464037639415220106">"256K mỗi bộ đệm nhật ký"</item>
- <item msgid="8539423820514360724">"1M mỗi bộ đệm nhật ký"</item>
- <item msgid="1984761927103140651">"4M mỗi bộ đệm nhật ký"</item>
- <item msgid="7892098981256010498">"16M mỗi bộ đệm nhật ký"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Tắt"</item>
<item msgid="6014837961827347618">"Tất cả"</item>
diff --git a/packages/SettingsLib/res/values-zh-rCN/arrays.xml b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
index 29d04e9..dc0ca10 100644
--- a/packages/SettingsLib/res/values-zh-rCN/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">",使用中(媒体)"</item>
<item msgid="5001852592115448348">",使用中(手机)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"关闭"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"关闭"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"关闭"</item>
- <item msgid="4195153527464162486">"每个日志缓冲区 64K"</item>
- <item msgid="7464037639415220106">"每个日志缓冲区 256K"</item>
- <item msgid="8539423820514360724">"每个日志缓冲区 1M"</item>
- <item msgid="1984761927103140651">"每个日志缓冲区 4M"</item>
- <item msgid="7892098981256010498">"每个日志缓冲区 16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"关闭"</item>
<item msgid="6014837961827347618">"全部"</item>
diff --git a/packages/SettingsLib/res/values-zh-rHK/arrays.xml b/packages/SettingsLib/res/values-zh-rHK/arrays.xml
index e7e2f84..cae08a6 100644
--- a/packages/SettingsLib/res/values-zh-rHK/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">",使用中 (媒體)"</item>
<item msgid="5001852592115448348">",使用中 (手機)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"關閉"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"關閉"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"關閉"</item>
- <item msgid="4195153527464162486">"每個記錄緩衝區 64K"</item>
- <item msgid="7464037639415220106">"每個記錄緩衝區 256K"</item>
- <item msgid="8539423820514360724">"每個記錄緩衝區 1M"</item>
- <item msgid="1984761927103140651">"每個記錄緩衝區 4M"</item>
- <item msgid="7892098981256010498">"每個記錄緩衝區 16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"關閉"</item>
<item msgid="6014837961827347618">"全部"</item>
diff --git a/packages/SettingsLib/res/values-zh-rTW/arrays.xml b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
index 0fdc14e..959d022 100644
--- a/packages/SettingsLib/res/values-zh-rTW/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">",使用中 (媒體)"</item>
<item msgid="5001852592115448348">",使用中 (手機)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"關閉"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"關閉"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"關閉"</item>
- <item msgid="4195153527464162486">"每個記錄緩衝區 64K"</item>
- <item msgid="7464037639415220106">"每個記錄緩衝區 256K"</item>
- <item msgid="8539423820514360724">"每個記錄緩衝區 1M"</item>
- <item msgid="1984761927103140651">"每個記錄緩衝區 4M"</item>
- <item msgid="7892098981256010498">"每個記錄緩衝區 16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"關閉"</item>
<item msgid="6014837961827347618">"全部"</item>
diff --git a/packages/SettingsLib/res/values-zu/arrays.xml b/packages/SettingsLib/res/values-zu/arrays.xml
index 2d43c67..78079e8 100644
--- a/packages/SettingsLib/res/values-zu/arrays.xml
+++ b/packages/SettingsLib/res/values-zu/arrays.xml
@@ -155,28 +155,14 @@
<item msgid="253388653486517049">", iyasebenza (imidiya)"</item>
<item msgid="5001852592115448348">", iyasebenza (ifoni)"</item>
</string-array>
- <string-array name="select_logd_size_titles">
- <item msgid="1191094707770726722">"Valiwe"</item>
- <item msgid="7839165897132179888">"64K"</item>
- <item msgid="2715700596495505626">"256K"</item>
- <item msgid="7099386891713159947">"1M"</item>
- <item msgid="6069075827077845520">"4M"</item>
- <item msgid="8243549501764402572">"16M"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Valiwe"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <string-array name="select_logd_size_summaries">
- <item msgid="409235464399258501">"Valiwe"</item>
- <item msgid="4195153527464162486">"64K ngebhafa yelogu ngayinye"</item>
- <item msgid="7464037639415220106">"256K ngebhafa yelogu ngayinye"</item>
- <item msgid="8539423820514360724">"1M ngebhafa yelogu ngayi"</item>
- <item msgid="1984761927103140651">"4M ngebhafa yelogu ngayinye"</item>
- <item msgid="7892098981256010498">"16M ngebhafa yelogu ngayinye"</item>
- </string-array>
+ <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Valiwe"</item>
<item msgid="6014837961827347618">"Konke"</item>
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index d278c59..86aa214 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 @@
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 c873e30..546642d 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 @@
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 41cc835..859f5a6 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -119,6 +119,7 @@
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.CREATE_USERS" />
+ <uses-permission android:name="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP" />
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
<uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
<uses-permission android:name="android.permission.ACCESS_LOWPAN_STATE"/>
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml b/packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml
index 3790378..8e37686 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 0000000..0161bcb
--- /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 0000000..0ce7a58
--- /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 0000000..0e308d2
--- /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 656d2e4..6ed3a0ae 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 170f2c4..6b42705 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 317e0e8..ba39d1e 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Invoermetode"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Ligging"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Ligging af"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Mediatoestel"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Net noodoproepe"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index b7b24db..a193bbb 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"የግቤት ስልት"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"አካባቢ"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"አካባቢ ጠፍቷል"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"የሚዲያ መሣሪያ"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"የአደጋ ጊዜ ጥሪዎች ብቻ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index ca823ed..a634281dc 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -348,6 +348,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"أسلوب الإدخال"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"الموقع الجغرافي"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"الموقع قيد الإيقاف"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"جهاز الوسائط"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"مكالمات طوارئ فقط"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 8e6fd51..1151644 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"ইনপুট পদ্ধতি"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"অৱস্থান"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"অৱস্থান অফ"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"মিডিয়া ডিভাইচ"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"জৰুৰীকালীন কল মাত্ৰ"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index d9a2ae1..146add2f 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Daxiletmə metodu"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Yer"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Yer Deaktiv"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media cihazı"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Yalnız təcili zənglər"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index ef3a1cf..77b101c 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -345,6 +345,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Metod unosa"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Lokacija je isključena"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medijski uređaj"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Samo hitni pozivi"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index a1804d1..5977d04 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -346,6 +346,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Метад уводу"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Месцазнаходжанне"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Вызначэнне месцазнаходжання адключана"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Мультымедыйная прылада"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Толькі экстранныя выклікі"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 6b59b24..53eeac8 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Метод на въвеждане"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Местоположение"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Местоположението е изключено"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Мултимедийно устройство"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"Индикатор за силата на получения сигнал (RSSI)"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Само спешни обаждания"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index a6344d0..b944dde 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"ইনপুট পদ্ধতি"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"লোকেশন"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"লোকেশন বন্ধ করা আছে"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"মিডিয়া ডিভাইস"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"শুধুমাত্র জরুরি কল"</string>
@@ -354,12 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"নতুন ব্যবহারকারী"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"ওয়াই-ফাই"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"ইন্টারনেট"</string>
- <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
- <skip />
- <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
- <skip />
- <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
- <skip />
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"বিমানের জন্য নিরাপদ"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"নেটওয়ার্ক উপলভ্য"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"নেটওয়ার্ক উপলভ্য নেই"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"সংযুক্ত নয়"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"কোনো নেটওয়ার্ক নেই"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ওয়াই-ফাই বন্ধ"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 0dc97fa..7717c65 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -345,6 +345,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Način unosa"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Utvrđivanje lokacije isključeno"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medijski uređaj"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Samo pozivi za hitne slučajeve"</string>
@@ -355,12 +359,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
- <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
- <skip />
- <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
- <skip />
- <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
- <skip />
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Sigurno za rad u zrakoplovu"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Mreže su dostupne"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Mreže nisu dostupne"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nije povezano"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nema mreže"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi je isključen"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index bd5815a6..f648bc6 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Mètode d\'introducció"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicació"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Ubicació desactivada"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositiu multimèdia"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Només trucades d\'emergència"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 41a33ef..4d14d2b 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -346,6 +346,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Metoda zadávání dat"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Poloha"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Poloha vypnuta"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Mediální zařízení"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Pouze tísňová volání"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 3d8b027..95b1a55 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Inputmetode"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Placering"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Placering fra"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medieenhed"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Kun nødopkald"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index a0e535f..e6acd87 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Eingabemethode"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Standort"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Standort aus"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Mediengerät"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Nur Notrufe"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 13dafa0..867c7d2 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Μέθοδος εισαγωγής"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Τοποθεσία"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Τοποθεσία απενεργοποιημένη"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Συσκευή μέσων"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Μόνο κλήσεις έκτακτης ανάγκης"</string>
@@ -354,12 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Νέος χρήστης"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Διαδίκτυο"</string>
- <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
- <skip />
- <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
- <skip />
- <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
- <skip />
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Ασφαλές για πτήση"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Υπάρχουν διαθέσιμα δίκτυα"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Δεν υπάρχουν διαθέσιμα δίκτυα"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Μη συνδεδεμένο"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Κανένα δίκτυο"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ανενεργό"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index dcee89d..185af57 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Input Method"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Location Off"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media device"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Emergency Calls Only"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 3217d1c..3517f87 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Input Method"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Location Off"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media device"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Emergency Calls Only"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index dcee89d..185af57 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Input Method"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Location Off"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media device"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Emergency Calls Only"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index dcee89d..185af57 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Input Method"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Location Off"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media device"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Emergency Calls Only"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 00fa584..306116c 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Input Method"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Location Off"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media device"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Emergency Calls Only"</string>
@@ -354,12 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
- <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
- <skip />
- <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
- <skip />
- <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
- <skip />
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Airplane-safe"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Networks available"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Networks unavailable"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index a552c76..27c424b 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Método de introducción"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicación"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Ubicación desactivada"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo multimedia"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Solo emergencia"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 4277c63..ab5d0d2 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Método de entrada"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicación"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Ubicación desactivada"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo multimedia"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Solo llamadas de emergencia"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 6429931..6c7e19a 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Sisestusmeetod"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Asukoht"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Asukoht on väljas"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Meediaseade"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Ainult hädaabikõned"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 664ae98..da7733c 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Idazketa-metodoa"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Kokapena"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Kokapena desaktibatuta"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Multimedia-gailua"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Larrialdi-deiak soilik"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 578fe88..2bc2e57 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"روش ورودی"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"مکان"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"مکان خاموش"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"دستگاه رسانه"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"فقط تماسهای اضطراری"</string>
@@ -354,12 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"کاربر جدید"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"اینترنت"</string>
- <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
- <skip />
- <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
- <skip />
- <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
- <skip />
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"ایمن در هواپیما"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"شبکه دردسترس است"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"شبکه دردسترس نیست"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"متصل نیست"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"شبکهای موجود نیست"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi خاموش است"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index d73b2eb..28708b9 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Syöttötapa"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Sijainti"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Sijainti ei käytössä"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medialaite"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Vain hätäpuhelut"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index df0e36c..8235638 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Mode de saisie"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Position"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Localisation désactivée"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Appareil multimédia"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Appels d\'urgence uniquement"</string>
@@ -354,12 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nouvel utilisateur"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
- <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
- <skip />
- <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
- <skip />
- <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
- <skip />
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Sécuritaire pour les avions"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Réseaux accessibles"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Aucun réseau accessible"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non connecté"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Aucun réseau"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi désactivé"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 64a2328..50a3cfb 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Mode de saisie"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Localisation désactivée"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Appareil multimédia"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Appels d\'urgence"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 75db845..4c6a53e 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Método de introdución de texto"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localización"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Localización desactivada"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo multimedia"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Só chamadas de emerxencia"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index c0d2557..add1ef5 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"ઇનપુટ પદ્ધતિ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"સ્થાન"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"સ્થાન બંધ"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"મીડિયા ઉપકરણ"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"ફક્ત ઇમર્જન્સી કૉલ"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 0bfa5ae..b4d61bb 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -346,6 +346,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"इनपुट विधि"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"जगह"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"जगह की जानकारी बंद है"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"मीडिया डिवाइस"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"सिर्फ़ आपातकालीन कॉल"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 33d0534..e5cf20a 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -345,6 +345,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Način unosa"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Lokacija je isključena"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medijski uređaj"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Samo hitni pozivi"</string>
@@ -355,12 +359,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
- <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
- <skip />
- <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
- <skip />
- <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
- <skip />
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Sigurno za rad u zrakoplovu"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Mreže su dostupne"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Mreže nisu dostupne"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nije povezano"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nema mreže"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi isključen"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 22b8fde..958e25f6 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Beviteli módszer"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Tartózkodási hely"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Hely kikapcsolva"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Médiaeszköz"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Csak segélyhívások"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 5e51c62..4fd1abe 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Մուտքագրման եղանակը"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Տեղորոշում"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Անջատել տեղադրությունը"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Մեդիա սարք"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Միայն շտապ կանչեր"</string>
@@ -354,12 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Նոր օգտատեր"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Ինտերնետ"</string>
- <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
- <skip />
- <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
- <skip />
- <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
- <skip />
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Ցանցեր, որոնք անվտանգ են ինքնաթիռում"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Հասանելի ցանցեր"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Անհասանելի ցանցեր"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Միացված չէ"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ցանց չկա"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi-ը անջատված է"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 9a217ce..4136ead 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Metode Masukan"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasi"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Lokasi Nonaktif"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Perangkat media"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Panggilan Darurat Saja"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 3f8e8fb..15c07e4 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Innsláttaraðferð"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Staðsetning"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Staðsetning óvirk"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Margmiðlunartæki"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Aðeins neyðarsímtöl"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 72cc5aa..299824b 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Metodo di immissione"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Geolocalizzazione"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Geolocalizz. non attiva"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo multimediale"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Solo chiamate di emergenza"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index d8c148a..e8cc8bf 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -346,6 +346,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"שיטת קלט"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"מיקום"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"מיקום כבוי"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"מכשיר מדיה"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"שיחות חירום בלבד"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 40a9323..459b520 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"入力方法"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"位置情報"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"現在地OFF"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"メディアデバイス"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"緊急通報のみ"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 6ab5510..20f2d03 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"შეყვანის მეთოდი"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"მდებარეობა"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"მდებარეობა გამორთულია"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"მედია მოწყობილობა"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"მხოლოდ გადაუდებელი დახმარების ზარებისთვის"</string>
@@ -354,12 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"ახალი მომხმარებელი"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"ინტერნეტი"</string>
- <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
- <skip />
- <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
- <skip />
- <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
- <skip />
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"თვითმფრინავისთვის უსაფრთხო"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"ქსელები ხელმისაწვდომია"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"ქსელები მიუწვდომელია"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"არ არის დაკავშირებული."</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ქსელი არ არის"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi გამორთულია"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index bda2f1a..65f3032 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Енгізу әдісі"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Орналасу"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Орын өшірулі"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Meдиа құрылғысы"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI (алынған сигнал қуатының көрсеткіші)"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Құтқару қызметіне ғана қоңырау шалынады"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 275a73a..a4f6e0a 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"វិធីសាស្ត្របញ្ចូល"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ទីតាំង"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"ទីតាំងបានបិទ"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"ឧបករណ៍មេឌៀ"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"ការហៅទៅលេខសង្គ្រោះបន្ទាន់តែប៉ុណ្ណោះ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 778ccca..e02ed5a 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"ಇನ್ಪುಟ್ ವಿಧಾನ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ಸ್ಥಳ"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"ಸ್ಥಳ ಆಫ್ ಆಗಿದೆ"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"ಮಾಧ್ಯಮ ಸಾಧನ"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"ತುರ್ತು ಕರೆಗಳು ಮಾತ್ರ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index e602ede..e9d33fa 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"입력 방법"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"위치"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"위치 사용 중지"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"미디어 기기"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"긴급 통화만 허용"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 0a2e8ae..f4191d4 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -346,6 +346,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Киргизүү ыкмасы"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Жайгашкан жер"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Жайгашытрууну өчүрүү"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Медиа түзмөгү"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Кырсыктаганда гана чалуу"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 0f320a5..0f0236f 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"ວິທີການປ້ອນຂໍ້ມູນ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ສະຖານທີ່"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"ຂໍ້ມູນສະຖານທີ່ປິດຢູ່"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"ອຸປະກອນສື່"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"ໂທສຸກເສີນເທົ່ານັ້ນ"</string>
@@ -354,12 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"ຜູ່ໃຊ້ໃໝ່"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"ອິນເຕີເນັດ"</string>
- <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
- <skip />
- <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
- <skip />
- <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
- <skip />
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"ປອດໄພກັບໃນຍົນ"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"ມີເຄືອຂ່າຍທີ່ສາມາດໃຊ້ໄດ້"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"ບໍ່ມີເຄືອຂ່າຍທີ່ສາມາດໃຊ້ໄດ້"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ບໍ່ໄດ້ເຊື່ອມຕໍ່"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ບໍ່ມີເຄືອຂ່າຍ"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ປິດ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 29f615e..8d9051d 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -346,6 +346,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Įvesties metodas"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Vietovė"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Vietovė išjungta"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medijos įrenginys"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Tik skambučiai pagalbos numeriu"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index f209ba8..1cde532 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -345,6 +345,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Ievades metode"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Atrašanās vieta"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Atrašanās vieta izslēgta"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Multivides ierīce"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Tikai ārkārtas izsaukumi"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 001e6a0..a006bc9 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Метод на внес"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Локација"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Исклучи локација"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Медиумски уред"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Само итни повици"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index e2ca3bb..4d2898f 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"ടൈപ്പുചെയ്യൽ രീതി"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ലൊക്കേഷൻ"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"ലൊക്കേഷൻ ഓഫാണ്"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"മീഡിയ ഉപകരണം"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"അടിയന്തിര കോളുകൾ മാത്രം"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 0b1a1ff..8845b07 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Оруулах арга"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Байршил"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Байршил идэвхгүй"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Медиа төхөөрөмж"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Зөвхөн яаралтай дуудлага"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 9ed6bb3..48498b7 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"इनपुट पद्धत"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"स्थान"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"स्थान बंद"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"मीडिया डिव्हाइस"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"फक्त आणीबाणीचे कॉल"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 81c15b0..4204417 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Kaedah Input"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasi"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Lokasi Dimatikan"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Peranti media"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Panggilan Kecemasan Sahaja"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 6bccb33..9b7488e 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"ထည့်သွင်းရန်နည်းလမ်း"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"တည်နေရာ"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"တည်နေရာပြမှု မရှိ"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"မီဒီယာ စက်ပစ္စည်း"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"အရေးပေါ်ခေါ်ဆိုမှုများသာ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 90defaa..490ba99 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Inndatametode"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Sted"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Posisjon av"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medieenhet"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Bare nødanrop"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 0e86b94..cf267fd 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"आगत विधि"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"स्थान"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"स्थान बन्द छ"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"मिडिया उपकरण"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"आपत्कालीन कल मात्र"</string>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 672d2f6..c4cf440 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 a216c49..ddb9abc 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Invoermethode"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Locatie"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Locatie uit"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media-apparaat"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Alleen noodoproepen"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index ed38d14..ce6723e 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"ଇନପୁଟ୍ ପଦ୍ଧତି"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ଲୋକେସନ୍"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"ଲୋକେସନ୍ ଅଫ୍"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"ମିଡିଆ ଡିଭାଇସ୍"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"କେବଳ ଜରୁରୀକାଳୀନ କଲ୍"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 3026a18..ad40d6c 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"ਇਨਪੁੱਟ ਵਿਧੀ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ਟਿਕਾਣਾ"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਬੰਦ"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"ਮੀਡੀਆ ਡੀਵਾਈਸ"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"ਸਿਰਫ਼ ਸੰਕਟਕਾਲੀਨ ਕਾਲਾਂ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 12422e9..75abfda 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -346,6 +346,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Metoda wprowadzania"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokalizacja"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Lokalizacja wyłączona"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Urządzenie multimedialne"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Tylko połączenia alarmowe"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 0d65c01..1ab1002 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Método de entrada"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Localização desativada"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo de mídia"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Chamadas de emergência"</string>
@@ -354,12 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo usuário"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
- <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
- <skip />
- <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
- <skip />
- <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
- <skip />
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Segura para aviões"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Redes disponíveis"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Redes indisponíveis"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Não conectado"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sem rede"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desligado"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 0932a96..06b67fb 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Método de Introdução"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Localização Desativada"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo multimédia"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Apenas chamadas de emergência"</string>
@@ -354,12 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo utilizador"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
- <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
- <skip />
- <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
- <skip />
- <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
- <skip />
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Seguras para aviões"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Redes disponíveis"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Redes indisponíveis"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Não Ligado"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sem Rede"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Desligado"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 0d65c01..1ab1002 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Método de entrada"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Localização desativada"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispositivo de mídia"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Chamadas de emergência"</string>
@@ -354,12 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo usuário"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
- <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
- <skip />
- <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
- <skip />
- <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
- <skip />
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Segura para aviões"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Redes disponíveis"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Redes indisponíveis"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Não conectado"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sem rede"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desligado"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index e181945..54d53af 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -345,6 +345,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Metodă de introducere"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Locație"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Localizarea este dezactivată"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Dispozitiv media"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Numai apeluri de urgență"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 5d08e3a..b6c2812 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -346,6 +346,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Способ ввода"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Геолокация"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Местоположение выкл."</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Режим медиа"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Экстр. вызов"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 7230a06..c1ba12b 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"ආදාන ක්රමය"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ස්ථානය"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"ස්ථානය අක්රියයි"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"මාධ්ය උපාංගය"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"හදිසි ඇමතුම් පමණි"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 1454884..dfa371b 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -346,6 +346,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Metóda vstupu"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Poloha"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Poloha vypnutá"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Mediálne zariadenie"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Len tiesňové volania"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 2cecff7..5f5ee65 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -346,6 +346,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Način vnosa"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Lokacija izklopljena"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Predstavnostna naprava"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Le klici v sili"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 3179e99..e1ca274 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Metoda e hyrjes"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Vendndodhja"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Vendndodhja është e çaktivizuar"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Pajisje e jashtme ruajtëse"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Vetëm telefonata urgjence"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index ea862bc..d7bc5a1 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -345,6 +345,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Метод уноса"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Локација"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Локација је искључена"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Медијски уређај"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Само хитни позиви"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 66685e9..3e38bdd 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Inmatningsmetod"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Plats"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Plats har inaktiverats"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medieenhet"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Endast nödsamtal"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 1563c76..5d1698d 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Mbinu ya uingizaji"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Kutambua Mahali"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Kitambua eneo kimezimwa"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Kifaa cha faili"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Simu za Dharura Pekee"</string>
@@ -354,12 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Mtumiaji mpya"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Intaneti"</string>
- <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
- <skip />
- <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
- <skip />
- <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
- <skip />
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Hali salama ya ndegeni"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Mitandao inapatikana"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Mitandao haipatikani"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Haijaunganishwa"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Hakuna Mtandao"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Imezimwa"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index a54b1a2..ebb7fa6 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"உள்ளீட்டு முறை"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"இருப்பிடம்"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"இருப்பிடத்தை முடக்கு"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"மீடியா சாதனம்"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"அவசரகால அழைப்புகள் மட்டும்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 01c3d7d1..1456a8c 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"ఇన్పుట్ పద్ధతి"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"లొకేషన్"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"లొకేషన్ ఆఫ్లో ఉంది"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"ప్రసార మాధ్యమ పరికరం"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"ఎమర్జెన్సీ కాల్స్ మాత్రమే"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index e51c308..65ee106 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"วิธีป้อนข้อมูล"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ตำแหน่ง"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"ปิดตำแหน่ง"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"อุปกรณ์สื่อ"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"โทรฉุกเฉินเท่านั้น"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 7b8070b..9eab088 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Pamamaraan ng Pag-input"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasyon"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Naka-off ang Lokasyon"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Device ng media"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Mga Pang-emergency na Tawag Lamang"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index f9e78c7..921cce8 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Giriş Yöntemi"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Konum"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Konum Bilgisi Kapalı"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Medya cihazı"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Yalnızca Acil Çağrılar İçin"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 72046e1..66a8cfb 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -346,6 +346,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Метод введення"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Місцезнаходження"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Місцезнаходження вимкнено"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Носій"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Екстрені виклики"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 9ccc133..3865bc0 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"ان پٹ کا طریقہ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"مقام"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"مقام آف"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"میڈیا آلہ"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"صرف ہنگامی کالیں"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 67d801b4..d6061a6 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Kiritish usuli"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Joylashuv"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Joylashuvni aniqlash xizmati yoqilmagan"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media qurilma"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Favqulodda chaqiruvlar"</string>
@@ -354,12 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Yangi foydalanuvchi"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
- <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
- <skip />
- <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
- <skip />
- <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
- <skip />
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Samolyot uchun xavfsiz"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Tarmoqlar mavjud"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Tarmoqqa ulanish imkonsiz"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ulanmagan"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tarmoq mavjud emas"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi o‘chiq"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 1d836e8..23e72c1 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Phương thức nhập"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Vị trí"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Tắt vị trí"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Thiết bị phương tiện"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Chỉ cuộc gọi khẩn cấp"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index fa6a510..55b524e 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"输入法"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"位置信息"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"位置信息:关闭"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"媒体设备"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"只能拨打紧急呼救电话"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index b69d097..701f32d 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"輸入法"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"位置"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"位置資訊已關閉"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"媒體裝置"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"只可撥打緊急電話"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 2fb0b86..4210ec1 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"輸入法"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"定位"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"定位服務已關閉"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"媒體裝置"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"僅可撥打緊急電話"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index e9e1a75..940fc19 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Indlela yokungenayo"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Indawo"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Indawo ivaliwe"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Idivayisi yemidiya"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Amakholi aphuthumayo kuphela"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 101124e..93d2f75 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 72dd724..d104d17 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -619,7 +619,7 @@
<dimen name="z_distance_between_notifications">0.5dp</dimen>
<!-- The height of the divider between the individual notifications. -->
- <dimen name="notification_divider_height">4dp</dimen>
+ <dimen name="notification_divider_height">2dp</dimen>
<!-- The corner radius of the shadow behind the notification. -->
<dimen name="notification_shadow_radius">0dp</dimen>
@@ -1180,10 +1180,9 @@
<dimen name="new_qs_vertical_margin">8dp</dimen>
<!-- Size of media cards in the QSPanel carousel -->
- <dimen name="qs_media_width">350dp</dimen>
<dimen name="qs_media_padding">16dp</dimen>
<dimen name="qs_media_panel_outer_padding">16dp</dimen>
- <dimen name="qs_media_album_size">52dp</dimen>
+ <dimen name="qs_media_album_size">120dp</dimen>
<dimen name="qs_media_icon_size">16dp</dimen>
<dimen name="qs_center_guideline_padding">10dp</dimen>
<dimen name="qs_seamless_icon_size">@dimen/qs_media_icon_size</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7cdd8b1..ac2e342 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 ee958f2..f834d6d 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 d5a02c2..d89e0eb 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 6eec5dc..902de23 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 e4427f4..57a4dab 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.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 @@
* @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 d672c2c..325e268 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 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 02e509e..f105fce 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 @@
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 @@
}
};
}
+
+ 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 a1c1f93..f88e38a 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.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 @@
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 @@
}
/**
+ * 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 @@
mStartLeash.release();
}
}
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.aidl
similarity index 64%
copy from media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
copy to packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.aidl
index edf96dd..1550ab3 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.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,6 @@
* limitations under the License.
*/
-package android.media.tv.tunerresourcemanager;
+package com.android.systemui.shared.system;
-/**
- * Simple container of the FrontendInfo struct defined in the TunerHAL 1.0 interface.
- *
- * @hide
- */
-parcelable TunerFrontendInfo {
- int handle;
-
- int frontendType;
-
- int exclusiveGroupId;
-}
+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 0000000..5c27b89
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.system;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+import android.window.IRemoteTransition;
+import android.window.TransitionFilter;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Wrapper to expose RemoteTransition (shell transitions) to Launcher.
+ *
+ * @see IRemoteTransition
+ * @see TransitionFilter
+ */
+@DataClass
+public class RemoteTransitionCompat implements Parcelable {
+ @NonNull final IRemoteTransition mTransition;
+ @Nullable TransitionFilter mFilter = null;
+
+ RemoteTransitionCompat(IRemoteTransition transition) {
+ mTransition = transition;
+ }
+
+ /** Adds a filter check that restricts this remote transition to home open transitions. */
+ public void addHomeOpenCheck() {
+ if (mFilter == null) {
+ mFilter = new TransitionFilter();
+ }
+ mFilter.mRequirements =
+ new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()};
+ mFilter.mRequirements[0].mActivityType = ACTIVITY_TYPE_HOME;
+ mFilter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+ }
+
+
+
+ // Code below generated by codegen v1.0.21.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ /* package-private */ RemoteTransitionCompat(
+ @NonNull IRemoteTransition transition,
+ @Nullable TransitionFilter filter) {
+ this.mTransition = transition;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mTransition);
+ this.mFilter = filter;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull IRemoteTransition getTransition() {
+ return mTransition;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable TransitionFilter getFilter() {
+ return mFilter;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mFilter != null) flg |= 0x2;
+ dest.writeByte(flg);
+ dest.writeStrongInterface(mTransition);
+ if (mFilter != null) dest.writeTypedObject(mFilter, flags);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected RemoteTransitionCompat(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ IRemoteTransition transition = IRemoteTransition.Stub.asInterface(in.readStrongBinder());
+ TransitionFilter filter = (flg & 0x2) == 0 ? null : (TransitionFilter) in.readTypedObject(TransitionFilter.CREATOR);
+
+ this.mTransition = transition;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mTransition);
+ this.mFilter = filter;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<RemoteTransitionCompat> CREATOR
+ = new Parcelable.Creator<RemoteTransitionCompat>() {
+ @Override
+ public RemoteTransitionCompat[] newArray(int size) {
+ return new RemoteTransitionCompat[size];
+ }
+
+ @Override
+ public RemoteTransitionCompat createFromParcel(@NonNull android.os.Parcel in) {
+ return new RemoteTransitionCompat(in);
+ }
+ };
+
+ /**
+ * A builder for {@link RemoteTransitionCompat}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static class Builder {
+
+ private @NonNull IRemoteTransition mTransition;
+ private @Nullable TransitionFilter mFilter;
+
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder(
+ @NonNull IRemoteTransition transition) {
+ mTransition = transition;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mTransition);
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Builder setTransition(@NonNull IRemoteTransition value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mTransition = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Builder setFilter(@NonNull TransitionFilter value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mFilter = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull RemoteTransitionCompat build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mFilter = null;
+ }
+ RemoteTransitionCompat o = new RemoteTransitionCompat(
+ mTransition,
+ mFilter);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x4) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1606862689344L,
+ codegenVersion = "1.0.21",
+ sourceFile = "frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java",
+ inputSignatures = "final @android.annotation.NonNull com.android.systemui.shared.system.IRemoteTransition mTransition\n @android.annotation.Nullable android.window.TransitionFilter mFilter\npublic void addHomeOpenCheck()\nclass RemoteTransitionCompat extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index deca14a..19520df 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.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 @@
.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 @@
.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 e6ad1cb..13da2b0 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 @@
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 61951cc..169a9c0 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 @@
} else if (mLauncherShowing) {
phoneState = getPhoneLauncherState();
} else {
- phoneState = getPhoneAppState();
+ phoneState = PHONE_STATE_APP_IMMERSIVE;
}
return phoneState;
}
@@ -161,16 +161,6 @@
}
}
- 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 @@
}
}
- 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 e4f6d6c..9b09cfd 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 @@
@Override
protected void handleResetAfterError() {
- resetErrorView(mContext, mIndicatorView);
+ resetErrorView();
}
@Override
protected void handleResetAfterHelp() {
- resetErrorView(mContext, mIndicatorView);
+ resetErrorView();
}
@Override
@@ -185,7 +185,7 @@
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 @@
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 176e9e6..45ee4ad 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 @@
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 c748ab2..18206ef 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 @@
* 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 @@
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 @@
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 dcb6ea3..1cafb4c 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 @@
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 @@
}
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 a42ab58..c2f80d4 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 @@
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 @@
}
canvas.restore();
+
+ if (mShowScrimAndDot && mRunAfterShowingScrimAndDot != null) {
+ post(mRunAfterShowingScrimAndDot);
+ mRunAfterShowingScrimAndDot = null;
+ }
}
RectF getSensorRect() {
@@ -294,6 +303,10 @@
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 82d313e..062410a 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.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 @@
@BindsInstance
Builder setShellCommandHandler(Optional<ShellCommandHandler> shellDump);
+ @BindsInstance
+ Builder setTransitions(Transitions t);
+
SysUIComponent build();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 7ca8e63..239a77e 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.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.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 @@
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 ced606c..60b665f 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.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 @@
@WMSingleton
Optional<TaskViewFactory> getTaskViewFactory();
+
+ /** Gets transitions */
+ @WMSingleton
+ Transitions getTransitions();
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index c18a6a4..743ac86 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.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 @@
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 @@
// 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 @@
mViewHolder.getSeamless().setOnClickListener(v -> {
mMediaOutputDialogFactory.create(data.getPackageName(), true);
});
+ TextView mDeviceName = mViewHolder.getSeamlessText();
+ mDeviceName.setVisibility(mShowDeviceName ? View.VISIBLE : View.GONE);
+ setVisibleAndAlpha(collapsedSet, R.id.media_seamless_text, mShowDeviceName);
+ setVisibleAndAlpha(expandedSet, R.id.media_seamless_text, mShowDeviceName);
ImageView iconView = mViewHolder.getSeamlessIcon();
TextView deviceName = mViewHolder.getSeamlessText();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index a23b07c..34d1f6e 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.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.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.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 @@
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 @@
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 @@
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 @@
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 @@
}
@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 @@
mLightBarController.onNavigationBarAppearanceChanged(appearance, nbModeChanged,
mNavigationBarMode, navbarColorManagedByIme);
}
+ if (mBehavior != behavior) {
+ mBehavior = behavior;
+ updateSystemUiStateFlags(-1);
+ }
}
@Override
@@ -1319,6 +1333,8 @@
.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 @@
return mNavigationBarWindowState == WINDOW_STATE_SHOWING;
}
+ private boolean allowSystemGestureIgnoringBarVisibility() {
+ return mBehavior != BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+ }
+
/**
* Checks current navigation bar mode and make transitions.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 5eba147..9e7ed0f 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 @@
*/
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 ba71fa6..9b3775e 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.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.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 @@
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 @@
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 @@
mUiModeNightTileProvider = uiModeNightTileProvider;
mScreenRecordTileProvider = screenRecordTileProvider;
mReduceBrightColorsTileProvider = reduceBrightColorsTileProvider;
+ mCameraToggleTileProvider = cameraToggleTileProvider;
+ mMicrophoneToggleTileProvider = microphoneToggleTileProvider;
}
public QSTile createTile(String tileSpec) {
@@ -198,6 +206,10 @@
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 0000000..98740a2
--- /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 0000000..8cc0d7b
--- /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 0000000..12205d6
--- /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 54e30af..760ebad 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.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.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.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 @@
private final ScreenshotHelper mScreenshotHelper;
private final Optional<OneHanded> mOneHandedOptional;
private final CommandQueue mCommandQueue;
+ private final Transitions mShellTransitions;
private Region mActiveNavBarRegion;
@@ -158,6 +162,7 @@
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 @@
}
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 @@
}
}
+ @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 @@
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 @@
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 @@
// 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 db2750b..9dce191 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.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 @@
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 @@
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 @@
* 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 @@
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
+ 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
- // 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);
+ // 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);
- PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, 0,
- editIntent, PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT);
+ PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
+ context, 0, editIntent, PendingIntent.FLAG_IMMUTABLE,
+ transition.bundle, UserHandle.CURRENT);
- // 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();
+ // 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);
+ // 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 7d57799..d77d1ea 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.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 @@
*/
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 void reset() {
uri = null;
shareTransition = null;
- editAction = null;
+ editTransition = null;
deleteAction = null;
smartActions = null;
}
@@ -352,6 +352,10 @@
}
}
+ boolean isPendingSharedTransition() {
+ return mScreenshotView.isPendingSharedTransition();
+ }
+
/**
* Release the constructed window context.
*/
@@ -626,7 +630,7 @@
}
mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mImageExporter,
- mScreenshotSmartActions, data, getShareTransitionSupplier());
+ mScreenshotSmartActions, data, getActionTransitionSupplier());
mSaveInBgTask.execute();
}
@@ -680,7 +684,7 @@
* 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 @@
}
@Override
- public void onFinish() { }
+ public void hideSharedElements() {
+ resetScreenshotView();
+ }
+
+ @Override
+ public void onFinish() {
+ }
};
Pair<ActivityOptions, ExitTransitionCoordinator> transition =
@@ -698,7 +708,7 @@
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 41c2098..211f507 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -74,7 +74,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
-import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ShareTransition;
+import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
import com.android.systemui.shared.system.QuickStepContract;
import java.util.ArrayList;
@@ -106,7 +106,6 @@
private static final long SCREENSHOT_DISMISS_Y_DURATION_MS = 350;
private static final long SCREENSHOT_DISMISS_ALPHA_DURATION_MS = 183;
private static final long SCREENSHOT_DISMISS_ALPHA_OFFSET_MS = 50; // delay before starting fade
- private static final long SCREENSHOT_DISMISS_SHARE_OFFSET_MS = 300; // delay after share clicked
private static final float SCREENSHOT_ACTIONS_START_SCALE_X = .7f;
private static final float ROUNDED_CORNER_RADIUS = .05f;
private static final int SWIPE_PADDING_DP = 12; // extra padding around views to allow swipe
@@ -142,7 +141,7 @@
private UiEventLogger mUiEventLogger;
private ScreenshotViewCallback mCallbacks;
private Animator mDismissAnimation;
- private boolean mIgnoreDismiss;
+ private boolean mPendingSharedTransition;
private final ArrayList<ScreenshotActionChip> mSmartChips = new ArrayList<>();
private PendingInteraction mPendingInteraction;
@@ -296,6 +295,10 @@
requestFocus();
}
+ View getScreenshotPreview() {
+ return mScreenshotPreview;
+ }
+
/**
* Set up the logger and callback on dismissal.
*
@@ -535,44 +538,22 @@
});
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.setPendingIntent(imageData.editAction.actionIntent,
- () -> {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED);
- animateDismissal();
- });
+ mEditChip.setOnClickListener(v -> {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED);
+ startSharedTransition(
+ imageData.editTransition.get());
+ });
mScreenshotPreview.setOnClickListener(v -> {
- try {
- imageData.editAction.actionIntent.send();
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "PendingIntent was cancelled", e);
- }
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED);
- animateDismissal();
+ startSharedTransition(
+ imageData.editTransition.get());
});
if (mPendingInteraction != null) {
@@ -611,14 +592,15 @@
return (mDismissAnimation != null && mDismissAnimation.isRunning());
}
+ boolean isPendingSharedTransition() {
+ return mPendingSharedTransition;
+ }
+
void animateDismissal() {
- animateDismissal(createScreenshotDismissAnimation());
+ animateDismissal(createScreenshotTranslateDismissAnimation());
}
private void animateDismissal(Animator dismissAnimation) {
- if (mIgnoreDismiss) {
- return;
- }
if (DEBUG_WINDOW) {
Log.d(TAG, "removing OnComputeInternalInsetsListener");
}
@@ -671,6 +653,7 @@
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
// Clear any references to the bitmap
mScreenshotPreview.setImageDrawable(null);
+ mPendingSharedTransition = false;
mActionsContainerBackground.setVisibility(View.GONE);
mActionsContainer.setVisibility(View.GONE);
mBackgroundProtection.setAlpha(0f);
@@ -698,7 +681,23 @@
mScreenshotSelectorView.stop();
}
- private AnimatorSet createScreenshotDismissAnimation() {
+ private void startSharedTransition(ActionTransition transition) {
+ try {
+ mPendingSharedTransition = true;
+ transition.action.actionIntent.send();
+
+ // fade out non-preview UI
+ createScreenshotFadeDismissAnimation().start();
+ } catch (PendingIntent.CanceledException e) {
+ mPendingSharedTransition = false;
+ if (transition.onCancelRunnable != null) {
+ transition.onCancelRunnable.run();
+ }
+ Log.e(TAG, "Intent cancelled", e);
+ }
+ }
+
+ private AnimatorSet createScreenshotTranslateDismissAnimation() {
ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1);
alphaAnim.setStartDelay(SCREENSHOT_DISMISS_ALPHA_OFFSET_MS);
alphaAnim.setDuration(SCREENSHOT_DISMISS_ALPHA_DURATION_MS);
@@ -725,6 +724,19 @@
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 c33bbc5..144ad39 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 @@
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 fbb8042..c4fa6df 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.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.SparseArray;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowInsetsController.Appearance;
+import android.view.WindowInsetsController.Behavior;
import androidx.annotation.NonNull;
@@ -90,7 +90,7 @@
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 @@
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 @@
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 @@
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 @@
}
@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 @@
}
@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 @@
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 @@
}
break;
}
- case MSG_TOP_APP_WINDOW_CHANGED: {
- args = (SomeArgs) msg.obj;
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).topAppWindowChanged(
- args.argi1, args.argi2 != 0, args.argi3 != 0);
- }
- args.recycle();
- break;
- }
case MSG_SHOW_INATTENTIVE_SLEEP_WARNING:
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).showInattentiveSleepWarning();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index e944249..6ba5215 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 @@
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 @@
}
@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 9f8fe35..a2e07b2 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 @@
/**
* 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 8223d97..a03fc13 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 @@
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 1a2550b..adeba90 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 void bindNotification(
+ @Action int selectedAction,
ShortcutManager shortcutManager,
PackageManager pm,
INotificationManager iNotificationManager,
@@ -224,7 +225,8 @@
@Background Handler bgHandler,
OnConversationSettingsClickListener onConversationSettingsClickListener,
Optional<BubblesManager> bubblesManagerOptional) {
- mSelectedAction = -1;
+ mPressedApply = false;
+ mSelectedAction = selectedAction;
mINotificationManager = iNotificationManager;
mOnUserInteractionCallback = onUserInteractionCallback;
mPackageName = pkg;
@@ -297,7 +299,8 @@
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 @@
@Override
public void onFinishedClosing() {
- // TODO: do we need to do anything here?
+ mSelectedAction = -1;
}
@Override
@@ -487,7 +490,7 @@
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 @@
}
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 2fd17a5..6a873b6 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 @@
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 2c501cb..b5963ef 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 @@
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 @@
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 9e8385b..6abbc6b 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 @@
}
}
+ 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 f4d01f3..5126f55 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 void setIsShadeOpening(boolean isOpening) {
+ mView.setIsShadeOpening(isOpening);
+ }
+
private final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() {
@Override
public void onMenuClicked(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
index d27a3d5..7d13405 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.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 @@
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 @@
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 5e71671..d132abe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -2408,6 +2408,11 @@
}
@Override
+ protected void setIsShadeOpening(boolean isOpening) {
+ mNotificationStackScrollLayoutController.setIsShadeOpening(isOpening);
+ }
+
+ @Override
protected void setOverExpansion(float overExpansion, boolean isPixels) {
if (mConflictingQsExpansionGesture || mQsExpandImmediate) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index c106518..a1112dc 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 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 62ded0a..da82986 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 @@
*/
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 f1b3cc5..fc1811b 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 @@
}
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 8f23824..8ea173b 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.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 @@
private boolean mTransientShown;
- private boolean mAppFullscreen;
- private boolean mAppImmersive;
-
private final DisplayMetrics mDisplayMetrics;
// XXX: gesture research
@@ -574,6 +572,8 @@
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 @@
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 @@
}
@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 @@
mStatusBarMode, navbarColorManagedByIme);
updateBubblesVisibility();
+ mStatusBarStateController.setFullscreenState(isFullscreen);
}
@Override
@@ -2432,16 +2432,6 @@
}
@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 @@
}
}
- /** 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 @@
// 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 @@
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 @@
@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 @@
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 f0efed3..00acd7b 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.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 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 2870152..5e8d590 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.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 @@
}
}
+ /**
+ * 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 88d0035..36a0e63 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 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 @@
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 1fdd816..d11e864 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 @@
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 @@
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 @@
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 @@
}
@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 @@
}
}
+ 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 @@
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 @@
// 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 1cc312a..5e88cd5 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 @@
}
@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 0000000..a76d08a
--- /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 0000000..231fe08
--- /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 101e3c6..2f66508 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 @@
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 a9c6016..f2b0d76 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 @@
*/
default void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork,
boolean noNetworksAvailable) {}
+
+ /**
+ * Callback for listeners to be able to update the no calling & SMS status
+ * @param noCalling whether the calling and SMS is not working.
+ * @param subId subscription ID for which to update the UI
+ */
+ default void setNoCallingStatus(boolean noCalling, int subId) {}
}
public interface EmergencyListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyController.java
index 6d5ce60..4a09234 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 @@
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 20cc46f..a2334f3 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 @@
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 a05fe1f..554145e 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 @@
}
}
+ 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 6a3a69c..ea80325 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 @@
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 069b405..7a4b912 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 @@
/** */
@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 56a4c203..df889f2 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.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.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 @@
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 08cd6e3..8d77c4a 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 @@
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 @@
if (count == null) {
Log.wtf(TAG, "Releasing WakeLock with invalid reason: " + why,
new Throwable());
+ return;
} else if (count == 1) {
mActiveClients.remove(why);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 715b0a2..81ac21c 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.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 @@
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 @@
@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 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 1b5877f..bbc238a 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 @@
@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 b24f4ab..c052563 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 @@
@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 @@
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 @@
mFgExecutor.runAllReady();
// WHEN fingerprint is requested because of AOD interrupt
mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
- // THEN the event is passed to the FingerprintManager
+ // THEN the scrim and dot is shown
+ verify(mUdfpsView).showScrimAndDot();
+ // AND a runnable that passes the event to FingerprintManager is set on the view
+ verify(mUdfpsView).setRunAfterShowingScrimAndDot(
+ mRunAfterShowingScrimAndDotCaptor.capture());
+ mRunAfterShowingScrimAndDotCaptor.getValue().run();
verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0),
eq(0), eq(3f) /* minor */, eq(2f) /* major */);
- // AND the scrim and dot is shown
- verify(mUdfpsView).showScrimAndDot();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
index 4d32a3b..b63274b 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.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 @@
@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 @@
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 ced8428..03f93fa 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 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 @@
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 @@
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 @@
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 d2d5708..2917dfa 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 @@
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.PromptInfo;
import android.os.Bundle;
import android.view.WindowInsetsController.Appearance;
+import android.view.WindowInsetsController.Behavior;
import androidx.test.filters.SmallTest;
@@ -116,24 +118,27 @@
}
@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 291b223..5c37656 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 @@
@Test
public void testBindNotification_SetsShortcutIcon() {
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -265,6 +266,7 @@
public void testBindNotification_SetsTextApplicationName() {
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -289,6 +291,7 @@
@Test
public void testBindNotification_SetsTextChannelName() {
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mLauncherApps,
mMockPackageManager,
@@ -316,6 +319,7 @@
mConversationChannel.setGroup(group.getId());
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -341,6 +345,7 @@
@Test
public void testBindNotification_GroupNameHiddenIfNoGroup() {
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -365,6 +370,7 @@
@Test
public void testBindNotification_noDelegate() {
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -400,6 +406,7 @@
.setShortcutInfo(mShortcutInfo)
.build();
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -425,6 +432,7 @@
public void testBindNotification_SetsOnClickListenerForSettings() {
final CountDownLatch latch = new CountDownLatch(1);
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -454,6 +462,7 @@
@Test
public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() {
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -478,6 +487,7 @@
public void testBindNotification_SettingsButtonInvisibleWhenDeviceUnprovisioned() {
final CountDownLatch latch = new CountDownLatch(1);
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -506,6 +516,7 @@
mConversationChannel.setImportance(IMPORTANCE_LOW);
mConversationChannel.setImportantConversation(true);
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -534,6 +545,7 @@
mConversationChannel.setImportantConversation(false);
mConversationChannel.setAllowBubbles(true);
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -565,6 +577,7 @@
mConversationChannel.setImportantConversation(false);
mConversationChannel.setAllowBubbles(true);
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -595,6 +608,7 @@
mConversationChannel.setImportantConversation(false);
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -639,6 +653,7 @@
mConversationChannel.setImportance(IMPORTANCE_LOW);
mConversationChannel.setImportantConversation(false);
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -682,6 +697,7 @@
mConversationChannel.setImportantConversation(false);
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -726,6 +742,7 @@
mConversationChannel.setImportantConversation(false);
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -763,6 +780,7 @@
mConversationChannel.setImportance(9);
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -799,6 +817,7 @@
mConversationChannel.setImportantConversation(true);
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -832,11 +851,75 @@
}
@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 @@
mConversationChannel.setOriginalImportance(IMPORTANCE_HIGH);
mConversationChannel.setImportantConversation(false);
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -909,6 +993,7 @@
mConversationChannel.setOriginalImportance(IMPORTANCE_HIGH);
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -944,6 +1029,7 @@
mConversationChannel.setAllowBubbles(true);
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -978,6 +1064,7 @@
@Test
public void testBindNotification_createsNewChannel() throws Exception {
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -1003,6 +1090,7 @@
public void testBindNotification_doesNotCreateNewChannelIfExists() throws Exception {
mNotificationChannel.setConversationId("", CONVERSATION_ID);
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -1038,6 +1126,7 @@
// GIVEN the user is changing conversation settings
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
@@ -1078,6 +1167,7 @@
when(b.build()).thenReturn(controller);
mNotificationInfo.bindNotification(
+ -1,
mShortcutManager,
mMockPackageManager,
mMockINotificationManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
index dbb4512..cdfab1e 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 @@
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 @@
@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 @@
}
@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 @@
}
@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 @@
}
@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 8e84f1a..51ce8e5 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.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.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.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.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.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 @@
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 3357be8..fe01f84 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 @@
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 @@
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 2e874a6..c0af15b 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.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 @@
}
@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 446d3f2..8b86403 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 @@
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 0000000..f48a95c
--- /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 c6f42f7..a31cfae 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 0000000..9c908c3
--- /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 6b45abd..7541833 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.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 @@
}
/**
+ * 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 @@
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 @@
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 @@
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 @@
}
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 @@
// 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 @@
}
}
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 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 @@
}
// 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 @@
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 @@
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 a6d9bf8..f04af8b 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.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 @@
// 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 dfcc325..b14ce1c 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -5,7 +5,7 @@
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 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 a3bcbbe..871de0d 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.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.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 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 @@
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 @@
/**
* 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 @@
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 @@
* 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 @@
}
}
+ 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 @@
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 @@
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;
+ 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);
+ }
}
- if (DEBUG) {
- Slog.i(TAG, "Pinned " + pf.fileName);
- }
- 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 @@
//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 @@
}
}
}
+
+ 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 9ba71dc..e99bb24 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -283,11 +283,16 @@
mIndividualEnabled.put(userId, userIndividualEnabled);
if (!enable) {
- // Remove any notifications prompting the user to disable sensory privacy
- NotificationManager notificationManager =
- mContext.getSystemService(NotificationManager.class);
+ long token = Binder.clearCallingIdentity();
+ try {
+ // Remove any notifications prompting the user to disable sensory privacy
+ NotificationManager notificationManager =
+ mContext.getSystemService(NotificationManager.class);
- notificationManager.cancel(sensor);
+ 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 c951fd4..cd6a9fb 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1442,6 +1442,9 @@
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 @@
}
}
-
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 @@
}
}
+ /*
+ * 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 b76f3278..dde182b 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -564,8 +564,7 @@
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 @@
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 c191a78..2fdc796 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.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 @@
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 6a9715e..2c83da5 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.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.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.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 @@
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,16 +102,40 @@
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 VibrationThread.VibrationCallbacks} that reports finished
+ * vibrations.
+ */
+ private final class VibrationCallbacks implements VibrationThread.VibrationCallbacks {
+
+ @Override
+ public void prepareSyncedVibration(int requiredCapabilities, int[] vibratorIds) {
+ }
+
+ @Override
+ public void triggerSyncedVibration(long vibrationId) {
+ }
+
+ @Override
+ public void onVibrationEnded(long vibrationId, Vibration.Status status) {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration thread finished with status " + status);
+ }
+ synchronized (mLock) {
+ mThread = null;
+ reportFinishVibrationLocked(status);
+ }
+ }
+ }
+
+ /**
* Implementation of {@link OnVibrationCompleteListener} with a weak reference to this service.
*/
private static final class VibrationCompleteListener implements OnVibrationCompleteListener {
@@ -127,41 +149,11 @@
public void onComplete(int vibratorId, long vibrationId) {
VibratorService service = mServiceRef.get();
if (service != null) {
- service.onVibrationComplete(vibrationId);
+ service.onVibrationComplete(vibratorId, vibrationId);
}
}
}
- /** 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 binderDied() {
- synchronized (mLock) {
- if (mVibration == mCurrentVibration) {
- if (DEBUG) {
- Slog.d(TAG, "Vibration finished because binder died, cleaning up");
- }
- doCancelVibrateLocked(Vibration.Status.CANCELLED);
- }
- }
- }
-
- private void linkToDeath() throws RemoteException {
- mVibration.token.linkToDeath(this, 0);
- }
-
- private void unlinkToDeath() {
- mVibration.token.unlinkToDeath(this, 0);
- }
- }
-
/** Holder for a {@link ExternalVibration}. */
private final class ExternalVibrationHolder {
@@ -262,13 +254,20 @@
/** 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");
}
- doCancelVibrateLocked(Vibration.Status.FINISHED);
+ 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);
+ }
}
}
}
@@ -354,6 +353,17 @@
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 @@
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 @@
endVibrationLocked(vib, Vibration.Status.IGNORED_BACKGROUND);
return;
}
- linkVibrationLocked(vib);
final long ident = Binder.clearCallingIdentity();
try {
doCancelVibrateLocked(Vibration.Status.CANCELLED);
@@ -474,6 +483,10 @@
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 @@
@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 @@
}
}
- // 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 @@
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 @@
@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 @@
}
}
- @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 @@
}
}
- 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 @@
}
}
- @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 @@
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 @@
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 88bb1a0..5cc3274 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 @@
}
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 @@
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 @@
// 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 @@
}
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 @@
// 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 @@
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 b587f1b..1f48aeb 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.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 @@
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 @@
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 @@
}
boolean isWhitelistedForFgsStartLocked(int uid) {
- final int appId = UserHandle.getAppId(uid);
- return Arrays.binarySearch(mDeviceIdleExceptIdleWhitelist, appId) >= 0
+ return Arrays.binarySearch(mDeviceIdleExceptIdleWhitelist, UserHandle.getAppId(uid)) >= 0
|| mFgsStartTempAllowList.isAllowed(uid);
}
@@ -8302,6 +8305,7 @@
synchronized(this) {
mConstants.dump(pw);
mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
+ mOomAdjuster.dumpCacheOomRankerSettings(pw);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
@@ -8722,6 +8726,7 @@
synchronized (this) {
mConstants.dump(pw);
mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
+ mOomAdjuster.dumpCacheOomRankerSettings(pw);
}
} else if ("services".equals(cmd) || "s".equals(cmd)) {
if (dumpClient) {
@@ -9296,6 +9301,8 @@
TimeUtils.formatDuration(ptw.duration, pw);
pw.print(" ");
pw.println(ptw.tag);
+ pw.print(" ");
+ pw.print(ptw.type);
}
}
}
@@ -15357,11 +15364,12 @@
*/
@GuardedBy("this")
void tempWhitelistUidLocked(int targetUid, long duration, String tag, int type) {
- mPendingTempWhitelist.put(targetUid, new PendingTempWhitelist(targetUid, duration, tag));
+ mPendingTempWhitelist.put(targetUid,
+ new PendingTempWhitelist(targetUid, duration, tag, type));
setUidTempWhitelistStateLocked(targetUid, true);
mUiHandler.obtainMessage(PUSH_TEMP_WHITELIST_UI_MSG).sendToTarget();
- if (type == BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
+ if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
mFgsStartTempAllowList.add(targetUid, duration);
}
}
@@ -15387,7 +15395,7 @@
for (int i = 0; i < N; i++) {
PendingTempWhitelist ptw = list[i];
mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid,
- ptw.duration, true, ptw.tag);
+ ptw.duration, ptw.type, true, ptw.tag);
}
}
@@ -15404,8 +15412,8 @@
}
@GuardedBy("this")
- final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) {
- mOomAdjuster.setAppIdTempWhitelistStateLocked(appId, onWhitelist);
+ final void setAppIdTempWhitelistStateLocked(int uid, boolean onWhitelist) {
+ mOomAdjuster.setAppIdTempWhitelistStateLocked(uid, onWhitelist);
}
@GuardedBy("this")
@@ -15713,6 +15721,16 @@
}
@Override
+ public boolean startProfile(@UserIdInt int userId) {
+ return mUserController.startProfile(userId);
+ }
+
+ @Override
+ public boolean stopProfile(@UserIdInt int userId) {
+ return mUserController.stopProfile(userId);
+ }
+
+ @Override
public UserInfo getCurrentUser() {
return mUserController.getCurrentUser();
}
@@ -15997,10 +16015,16 @@
}
@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 6fe934e..ada7eea 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 @@
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 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 @@
});
private final Context mContext;
+
+ @GuardedBy("mStats")
private final BatteryStatsImpl mStats;
@GuardedBy("this")
@@ -123,6 +130,7 @@
@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 @@
@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 @@
/** 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 @@
@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 @@
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 @@
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 @@
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 @@
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 2f7c523..405ee72 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.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.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 @@
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 @@
}
}
- @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 @@
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 @@
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 void systemServicesReady() {
mStats.systemServicesReady(mContext);
+ mWorker.systemServicesReady();
Watchdog.getInstance().addMonitor(this);
}
@@ -2270,7 +2196,6 @@
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 @@
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 0000000..26cfd62
--- /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 1f90393..3aca4cf 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 @@
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 @@
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 e5d57df..de79315 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.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.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 @@
* 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 @@
*/
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 @@
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 @@
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 @@
}
}
+ 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 @@
}
@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 @@
}
@GuardedBy("mService")
+ void dumpCacheOomRankerSettings(PrintWriter pw) {
+ mCacheOomRanker.dump(pw);
+ }
+
+ @GuardedBy("mService")
void updateAppFreezeStateLocked(ProcessRecord app) {
if (!mCachedAppOptimizer.useFreezer()) {
return;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index d4d0165..520a28b 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 @@
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 @@
}
void setCached(boolean cached) {
- mCached = cached;
+ if (mCached != cached) {
+ mCached = cached;
+ if (cached) {
+ ++mCacheOomRankerUseCount;
+ }
+ }
}
@Override
@@ -836,6 +848,10 @@
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 c04f6ff..d73de7c 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -628,19 +628,30 @@
Binder.getCallingUid(), Binder.getCallingPid(), userId);
}
- if (getUserInfo(userId).isManagedProfile()) {
+ final UserInfo userInfo = getUserInfo(userId);
+ if (userInfo.isProfile()) {
UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
if (parent != null) {
- final Intent profileUnlockedIntent = new Intent(
- Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
- profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
- profileUnlockedIntent.addFlags(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
- mInjector.broadcastIntent(profileUnlockedIntent,
- null, null, 0, null, null, null, AppOpsManager.OP_NONE,
- null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
- Binder.getCallingPid(), parent.id);
+ // Send PROFILE_ACCESSIBLE broadcast to the parent user if a profile was unlocked
+ broadcastProfileAccessibleStateChanged(userId, parent.id,
+ Intent.ACTION_PROFILE_ACCESSIBLE);
+
+ //TODO(b/175704931): send ACTION_MANAGED_PROFILE_AVAILABLE
+
+ // Also send MANAGED_PROFILE_UNLOCKED broadcast to the parent user
+ // if a managed profile was unlocked
+ if (userInfo.isManagedProfile()) {
+ final Intent profileUnlockedIntent = new Intent(
+ Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
+ profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
+ profileUnlockedIntent.addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ mInjector.broadcastIntent(profileUnlockedIntent,
+ null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
+ Binder.getCallingPid(), parent.id);
+ }
}
}
@@ -761,13 +772,44 @@
int restartUser(final int userId, final boolean foreground) {
return stopUser(userId, /* force= */ true, /* allowDelayedLocking= */ false,
/* stopUserCallback= */ null, new KeyEvictedCallback() {
- @Override
- public void keyEvicted(@UserIdInt int userId) {
- // Post to the same handler that this callback is called from to ensure the user
- // cleanup is complete before restarting.
- mHandler.post(() -> UserController.this.startUser(userId, foreground));
- }
- });
+ @Override
+ public void keyEvicted(@UserIdInt int userId) {
+ // Post to the same handler that this callback is called from to ensure
+ // the user cleanup is complete before restarting.
+ mHandler.post(() -> UserController.this.startUser(userId, foreground));
+ }
+ });
+ }
+
+ /**
+ * Stops a user only if it's a profile, with a more relaxed permission requirement:
+ * {@link android.Manifest.permission#MANAGE_USERS} or
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
+ * To be called from ActivityManagerService.
+ * @param userId the id of the user to stop.
+ * @return true if the operation was successful.
+ */
+ boolean stopProfile(final @UserIdInt int userId) {
+ if (mInjector.checkCallingPermission(android.Manifest.permission.MANAGE_USERS)
+ == PackageManager.PERMISSION_DENIED && mInjector.checkCallingPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ == PackageManager.PERMISSION_DENIED) {
+ throw new SecurityException(
+ "You either need MANAGE_USERS or INTERACT_ACROSS_USERS_FULL permission to "
+ + "stop a profile");
+ }
+
+ final UserInfo userInfo = getUserInfo(userId);
+ if (userInfo == null || !userInfo.isProfile()) {
+ throw new IllegalArgumentException("User " + userId + " is not a profile");
+ }
+
+ enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
+ synchronized (mLock) {
+ return stopUsersLU(userId, /* force= */ true, /* allowDelayedLocking= */
+ false, /* stopUserCallback= */ null, /* keyEvictedCallback= */ null)
+ == ActivityManager.USER_OP_SUCCESS;
+ }
}
int stopUser(final int userId, final boolean force, boolean allowDelayedLocking,
@@ -1144,6 +1186,17 @@
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
Binder.getCallingPid(), UserHandle.USER_ALL);
+
+ // Send PROFILE_INACCESSIBLE broadcast if a profile was stopped
+ final UserInfo userInfo = getUserInfo(userId);
+ if (userInfo.isProfile()) {
+ UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
+ if (parent != null) {
+ broadcastProfileAccessibleStateChanged(userId, parent.id,
+ Intent.ACTION_PROFILE_INACCESSIBLE);
+ //TODO(b/175704931): send ACTION_MANAGED_PROFILE_UNAVAILABLE
+ }
+ }
}
/**
@@ -1208,6 +1261,37 @@
}
}
+ /**
+ * Starts a user only if it's a profile, with a more relaxed permission requirement:
+ * {@link android.Manifest.permission#MANAGE_USERS} or
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
+ * To be called from ActivityManagerService.
+ * @param userId the id of the user to start.
+ * @return true if the operation was successful.
+ */
+ boolean startProfile(final @UserIdInt int userId) {
+ if (mInjector.checkCallingPermission(android.Manifest.permission.MANAGE_USERS)
+ == PackageManager.PERMISSION_DENIED && mInjector.checkCallingPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ == PackageManager.PERMISSION_DENIED) {
+ throw new SecurityException(
+ "You either need MANAGE_USERS or INTERACT_ACROSS_USERS_FULL permission to "
+ + "start a profile");
+ }
+
+ final UserInfo userInfo = getUserInfo(userId);
+ if (userInfo == null || !userInfo.isProfile()) {
+ throw new IllegalArgumentException("User " + userId + " is not a profile");
+ }
+
+ if (!userInfo.isEnabled()) {
+ Slog.w(TAG, "Cannot start disabled profile #" + userId);
+ return false;
+ }
+
+ return startUserNoChecks(userId, /* foreground= */ false, /* unlockListener= */ null);
+ }
+
boolean startUser(final @UserIdInt int userId, final boolean foreground) {
return startUser(userId, foreground, null);
}
@@ -1248,9 +1332,13 @@
final @UserIdInt int userId,
final boolean foreground,
@Nullable IProgressListener unlockListener) {
-
checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "startUser");
+ return startUserNoChecks(userId, foreground, unlockListener);
+ }
+
+ private boolean startUserNoChecks(final @UserIdInt int userId, final boolean foreground,
+ @Nullable IProgressListener unlockListener) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("startUser-" + userId + "-" + (foreground ? "fg" : "bg"));
@@ -1872,6 +1960,25 @@
}
}
+ /**
+ * 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 676fcd0..17fd32c 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 @@
private void writeHistoricalPackageOpsDLocked(@NonNull HistoricalPackageOps packageOps,
@NonNull TypedXmlSerializer serializer) throws IOException {
serializer.startTag(null, TAG_PACKAGE);
- serializer.attribute(null, ATTR_NAME, packageOps.getPackageName());
+ serializer.attributeInterned(null, ATTR_NAME, packageOps.getPackageName());
final int numAttributions = packageOps.getAttributedOpsCount();
for (int i = 0; i < numAttributions; i++) {
final AppOpsManager.AttributedHistoricalOps op = packageOps.getAttributedOpsAt(i);
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index e4d9052..52152ab 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 @@
@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 17ec112..85de81bb3 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 @@
@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 fd5ada0..3387049 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 @@
@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 @@
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 @@
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 @@
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 fa50388..55ac248 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 @@
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 c662df2..b3580fb 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.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 7c9fe38..f3c37ef 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 @@
* 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 @@
*
* @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 @@
* @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 @@
/**
* @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 @@
* @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 @@
}
/**
- * 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 @@
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 @@
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 fecc6f0..aa7faf5 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 @@
@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 @@
// 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 @@
}
@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 @@
}
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 @@
// 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 @@
// 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 @@
+ ", 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 @@
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 @@
*
* @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 @@
* @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 @@
/**
* @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 7f01fda..6a622c3 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.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 0000000..63cd412
--- /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 edc7edc..8529e81 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 @@
* {@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 @@
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 @@
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 176e49c..2693f2f 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 @@
/**
* 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 93728d0..630e5ea 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 @@
* {@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 e16b2f8..c97003b 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 @@
* 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 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 @@
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 cec6dde..4ea48fd 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 @@
* 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 35cbcc0..187193d 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.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 acf0f08..5fb194c 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.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 058bcf6..810489b1c 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.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 @@
@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 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 @@
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 @@
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 @@
// 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 @@
});
}
+ 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 @@
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 7edeb7b..f355158 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.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 @@
* 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 90df726..82ad387 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.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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
};
}
- @NonNull BaseClientMonitor.LazyDaemon<ISession> getLazySession() {
+ @NonNull HalClientMonitor.LazyDaemon<ISession> getLazySession() {
return mLazySession;
}
@@ -491,7 +492,7 @@
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 fbc26c6..50483d9 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.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 @@
@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 8593836..5e7ddeb 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.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 @@
@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 @@
.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 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 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 @@
@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 @@
@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 @@
@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 @@
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 @@
@Override
public long getAuthenticatorId(int sensorId, int userId) {
- return mAuthenticatorIds.get(userId);
+ return mAuthenticatorIds.getOrDefault(userId, 0L);
}
@Override
@@ -530,7 +531,7 @@
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 @@
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 @@
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 @@
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 @@
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 d30c9b1..442303b 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.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 bb83860..e0548e0 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.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 @@
* 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 0fb0de2..4356043 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.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 @@
* 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 78ee714..0e72f94 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.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 82dc161..c413c8b 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 @@
}
@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 cacc366..0864c1a 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 @@
}
@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 8170041..02d4ac3 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.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 d65ecff..8a666f9 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.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 @@
@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 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 @@
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 @@
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 @@
// 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 @@
});
}
+ 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 @@
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 @@
@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 @@
@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 3bdcc1d..cd84cdf 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.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 @@
* 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 c0f0577..911f6b4 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.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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
};
}
- @NonNull BaseClientMonitor.LazyDaemon<ISession> getLazySession() {
+ @NonNull HalClientMonitor.LazyDaemon<ISession> getLazySession() {
return mLazySession;
}
@@ -505,7 +506,7 @@
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 ddae110..ac4f665 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 @@
*/
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 @@
@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 e57e675..6cc8687 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.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 @@
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 @@
@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 @@
@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 @@
@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 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 @@
@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 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 @@
@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 @@
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 @@
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 @@
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 @@
@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 @@
@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 3ea2366..2394a70 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 @@
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 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 @@
@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 @@
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 784e37b..13e2e4f 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 @@
}
}
+ @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 b2e3c33..8493af1 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 @@
}
@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 2a4c2ef..f6ec4d9 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.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 @@
/**
* Sets the HAL's current active user, and updates the framework's authenticatorId cache.
*/
-public class FingerprintUpdateActiveUserClient extends BaseClientMonitor<IBiometricsFingerprint> {
+public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometricsFingerprint> {
private static final String TAG = "FingerprintUpdateActiveUserClient";
private static final String FP_DATA_DIR = "fpdata";
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index 1024556..26244e6 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 @@
@Override
public void logDefaultNetworkValidity(boolean valid) {
+ NetworkStack.checkNetworkStackPermission(getContext());
mDefaultNetworkMetrics.logDefaultNetworkValidity(SystemClock.elapsedRealtime(), valid);
}
@@ -375,6 +376,7 @@
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 c1b1b6a..952193b 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 @@
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 b0a73f1..ba6cbcd 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 @@
// 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 @@
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 @@
}
@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 @@
*
* @return the old capabilities of this network.
*/
- public synchronized NetworkCapabilities getAndSetNetworkCapabilities(
+ @NonNull public synchronized NetworkCapabilities getAndSetNetworkCapabilities(
@NonNull final NetworkCapabilities nc) {
final NetworkCapabilities oldNc = networkCapabilities;
networkCapabilities = nc;
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index a65f809..fb1e819 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 @@
mEnableTeardown = enableTeardown;
}
+ @VisibleForTesting
+ public boolean getEnableTeardown() {
+ return mEnableTeardown;
+ }
+
/**
* Update current state, dispatching event to listeners.
*/
@@ -2146,6 +2151,11 @@
// 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 60e4595..55103ca 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.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.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.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.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 @@
// 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 @@
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 @@
}
}
- 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 a127858..bb2fbed 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 @@
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 @@
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 @@
}
}
+ 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 521ce69..a0d9e8e 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.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 @@
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 @@
@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);
+ }
+ }
+
+ 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 SharedMemory getSerializedSystemFontMap() {
- if (!Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
- return null;
- }
+ 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 mCurrentFontSettings;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ 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;
}
- }
- @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);
+ public @NonNull FontConfig getSystemFontConfig() {
+ return mSystemFontConfig;
}
- return null;
- }
+
+ 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 0000000..7306471
--- /dev/null
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.graphics.fonts;
+
+import android.os.FileUtils;
+import android.util.Base64;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.HashMap;
+import java.util.Map;
+
+final class UpdatableFontDir {
+
+ private static final String TAG = "UpdatableFontDir";
+ private static final String RANDOM_DIR_PREFIX = "~~";
+
+ /** Interface to mock font file access in tests. */
+ interface FontFileParser {
+ long getVersion(File file) throws IOException;
+ }
+
+ /** Data class to hold font file path and version. */
+ static final class FontFileInfo {
+ final File mFile;
+ final long mVersion;
+
+ FontFileInfo(File file, long version) {
+ mFile = file;
+ mVersion = version;
+ }
+ }
+
+ /**
+ * Root directory for storing updated font files. Each font file is stored in a unique random
+ * dir. The font file path would be {@code mFilesDir/~~{randomStr}/{fontFileName}}.
+ */
+ private final File mFilesDir;
+ private final FontFileParser mParser;
+ @GuardedBy("UpdatableFontDir.this")
+ private final Map<String, FontFileInfo> mFontFileInfoMap = new HashMap<>();
+
+ UpdatableFontDir(File filesDir, FontFileParser parser) {
+ mFilesDir = filesDir;
+ mParser = parser;
+ loadFontFileMap();
+ }
+
+ private void loadFontFileMap() {
+ synchronized (UpdatableFontDir.this) {
+ mFontFileInfoMap.clear();
+ File[] dirs = mFilesDir.listFiles();
+ if (dirs == null) return;
+ for (File dir : dirs) {
+ if (!dir.getName().startsWith(RANDOM_DIR_PREFIX)) continue;
+ File[] files = dir.listFiles();
+ if (files == null || files.length != 1) continue;
+ addFileToMapLocked(files[0], true);
+ }
+ }
+ }
+
+ void installFontFile(String name, FileDescriptor fd) throws IOException {
+ // TODO: Validate name.
+ synchronized (UpdatableFontDir.this) {
+ // TODO: proper error handling
+ File newDir = getRandomDir(mFilesDir);
+ if (!newDir.mkdir()) {
+ throw new IOException("Failed to create a new dir");
+ }
+ File newFontFile = new File(newDir, name);
+ try (FileOutputStream out = new FileOutputStream(newFontFile)) {
+ FileUtils.copy(fd, out.getFD());
+ }
+ addFileToMapLocked(newFontFile, false);
+ }
+ }
+
+ /**
+ * Given {@code parent}, returns {@code parent/~~[randomStr]}.
+ * Makes sure that {@code parent/~~[randomStr]} directory doesn't exist.
+ * Notice that this method doesn't actually create any directory.
+ */
+ private static File getRandomDir(File parent) {
+ SecureRandom random = new SecureRandom();
+ byte[] bytes = new byte[16];
+ File dir;
+ do {
+ random.nextBytes(bytes);
+ String dirName = RANDOM_DIR_PREFIX
+ + Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP);
+ dir = new File(parent, dirName);
+ } while (dir.exists());
+ return dir;
+ }
+
+ private void addFileToMapLocked(File file, boolean deleteOldFile) {
+ final long version;
+ try {
+ version = mParser.getVersion(file);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read font file", e);
+ return;
+ }
+ if (version == -1) {
+ Slog.e(TAG, "Invalid font file");
+ return;
+ }
+ FontFileInfo info = mFontFileInfoMap.get(file.getName());
+ if (info == null) {
+ // TODO: check version of font in /system/fonts and /product/fonts
+ mFontFileInfoMap.put(file.getName(), new FontFileInfo(file, version));
+ } else if (info.mVersion < version) {
+ if (deleteOldFile) {
+ FileUtils.deleteContentsAndDir(info.mFile.getParentFile());
+ }
+ mFontFileInfoMap.put(file.getName(), new FontFileInfo(file, version));
+ }
+ }
+
+ Map<String, File> getFontFileMap() {
+ Map<String, File> map = new HashMap<>();
+ synchronized (UpdatableFontDir.this) {
+ for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) {
+ map.put(entry.getKey(), entry.getValue().mFile);
+ }
+ }
+ return map;
+ }
+}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index a207a96..23c70ee 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 @@
}
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 @@
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 @@
@Override // Binder call
public InputSensorInfo[] getSensorList(int deviceId) {
- InputSensorInfo[] sensors = nativeGetSensorList(mPtr, deviceId);
- return sensors;
+ return nativeGetSensorList(mPtr, deviceId);
}
@Override // Binder call
diff --git a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java
index b576810..5fa7998 100644
--- a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java
+++ b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java
@@ -21,6 +21,9 @@
import android.media.metrics.NetworkEvent;
import android.media.metrics.PlaybackErrorEvent;
import android.media.metrics.PlaybackMetrics;
+import android.media.metrics.PlaybackStateEvent;
+import android.media.metrics.TrackChangeEvent;
+import android.os.Binder;
import android.util.Base64;
import android.util.StatsEvent;
import android.util.StatsLog;
@@ -54,6 +57,33 @@
private final class BinderService extends IPlaybackMetricsManager.Stub {
@Override
public void reportPlaybackMetrics(String sessionId, PlaybackMetrics metrics, int userId) {
+ StatsEvent statsEvent = StatsEvent.newBuilder()
+ .setAtomId(320)
+ .writeInt(Binder.getCallingUid())
+ .writeString(sessionId)
+ .writeLong(metrics.getMediaDurationMillis())
+ .writeInt(metrics.getStreamSource())
+ .writeInt(metrics.getStreamType())
+ .writeInt(metrics.getPlaybackType())
+ .writeInt(metrics.getDrmType())
+ .writeInt(metrics.getContentType())
+ .writeString(metrics.getPlayerName())
+ .writeString(metrics.getPlayerVersion())
+ .writeByteArray(new byte[0]) // TODO: write experiments proto
+ .writeInt(metrics.getVideoFramesPlayed())
+ .writeInt(metrics.getVideoFramesDropped())
+ .writeInt(metrics.getAudioUnderrunCount())
+ .writeLong(metrics.getNetworkBytesRead())
+ .writeLong(metrics.getLocalBytesRead())
+ .writeLong(metrics.getNetworkTransferDurationMillis())
+ .usePooledBuffer()
+ .build();
+ StatsLog.write(statsEvent);
+ }
+
+ @Override
+ public void reportPlaybackStateEvent(
+ String sessionId, PlaybackStateEvent event, int userId) {
// TODO: log it to statsd
}
@@ -91,5 +121,30 @@
.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 ea1d8da..ea2788c 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.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 @@
@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 @@
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 @@
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 @@
try {
// Use the privileged method because Lockdown VPN is initiated by the system, so
// no additional permission checks are necessary.
- mVpn.startLegacyVpnPrivileged(mProfile, KeyStore.getInstance(), egressProp);
+ mVpn.startLegacyVpnPrivileged(mProfile, mKeyStore, egressProp);
} catch (IllegalStateException e) {
mAcceptedEgressIface = null;
Log.e(TAG, "Failed to start VPN", e);
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index cbd973a..61c8b17 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.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 @@
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 @@
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 @@
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 @@
if (existing.isDeleted()) {
// The existing channel was deleted - undelete it.
existing.setDeleted(false);
+ existing.setDeletedTimeMs(-1);
needsPolicyFileChange = true;
wasUndeleted = true;
@@ -1119,6 +1138,7 @@
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 @@
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 01eeb31..ef0f0ee 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.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.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 @@
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 @@
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 @@
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 @@
}
@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 @@
}
}
+ 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 b65fc73..2b5c393 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.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 @@
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 @@
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 @@
@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 @@
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 @@
}
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 @@
@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 @@
}
@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 @@
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 @@
throw new IllegalStateException("Duplicate checksums.");
}
- mChecksums.put(name, checksums);
+ mChecksums.put(name, new PerFileChecksum(checksums, signature));
}
}
@@ -3032,15 +3067,25 @@
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 @@
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 @@
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
"Failed to store digests for " + mPackageName, e);
}
-
- stageFileLocked(targetDigestsFile, targetDigestsFile);
}
@GuardedBy("mLock")
@@ -3704,7 +3757,16 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 de7338f..c93127d 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 @@
/**
* 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 @@
* 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 @@
* <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 @@
// 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 @@
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.
+ 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;
+ }
+ }
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;
- }
- }
- 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 9f07695..89729b5 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 @@
}
}
+ // 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 @@
* 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 95ce140..3c4457d 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 @@
* 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 7caf739..607c165 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.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 @@
boolean mWakeOnDpadKeyPress;
boolean mWakeOnAssistKeyPress;
boolean mWakeOnBackKeyPress;
+ long mWakeUpToLastStateTimeout;
private boolean mHandleVolumeKeysInWM;
@@ -1846,6 +1848,9 @@
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 @@
// 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 @@
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 @@
}
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 @@
}
}
+ 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 @@
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 @@
}
}
+ 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 0157706..17e81da 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 @@
private Sensor mSensor;
private OrientationJudge mOrientationJudge;
private int mCurrentRotation = -1;
+ private final Context mContext;
private final Object mLock = new Object();
@@ -88,6 +89,7 @@
* 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 @@
}
}
+ /**
+ * 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 b95c5efc..0000000
--- 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 0000000..097f332
--- /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 955e1cd..084dc32 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 @@
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 7778572..64bddcd 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 @@
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 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 @@
private final Injector mInjector;
private Context mContext;
- private IPowerStatsHALWrapper mPowerStatsHALWrapper;
@Nullable
private PowerStatsLogger mPowerStatsLogger;
@Nullable
@@ -65,6 +70,8 @@
@VisibleForTesting
static class Injector {
+ private IPowerStatsHALWrapper mPowerStatsHALWrapper;
+
File createDataStoragePath() {
return new File(Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM),
DATA_STORAGE_SUBDIR);
@@ -86,6 +93,13 @@
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 @@
}
} 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 @@
@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 @@
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/LegacyRoleHolderProvider.java
deleted file mode 100644
index ed0d675..0000000
--- a/services/core/java/com/android/server/role/LegacyRoleHolderProvider.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.role;
-
-import android.annotation.NonNull;
-import android.annotation.UserIdInt;
-
-import java.util.List;
-
-/**
- * A provider for migrating legacy "role"s to their actual role implementation.
- */
-//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
-public interface LegacyRoleHolderProvider {
- /**
- * 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.
- *
- * @param roleName the name of the role
- * @param userId the user ID
- * @return a list of holders for the given role
- */
- @NonNull
- List<String> getLegacyRoleHolders(@NonNull String roleName, @UserIdInt int userId);
-}
diff --git a/services/core/java/com/android/server/role/LegacyRoleStateProvider.java b/services/core/java/com/android/server/role/LegacyRoleStateProvider.java
new file mode 100644
index 0000000..ec4cfc1
--- /dev/null
+++ b/services/core/java/com/android/server/role/LegacyRoleStateProvider.java
@@ -0,0 +1,43 @@
+/*
+ * 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.role;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 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 LegacyRoleStateProvider {
+ /**
+ * Get the legacy role state stored in the platform.
+ *
+ * @param userId the user ID
+ * @return a mapping of role name to its set of holders
+ */
+ @NonNull
+ 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 4e42f16..bc5ddd3 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
- if (mPackageManagerInternal.getInstantAppPackageName(callingUid) != null) {
+ if (isInstantApp(callingUid)) {
return null;
}
@@ -676,6 +649,25 @@
}
}
+ 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 3a5ed5c..f5a79ea 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.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.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 @@
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 @@
* 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 @@
private void readFile() {
synchronized (mLock) {
- RolesState roles = mPersistence.readForUser(UserHandle.of(mUserId));
- if (roles == null) {
- readLegacyFileLocked();
- scheduleWriteFileLocked();
- return;
+ 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);
}
-
- mVersion = roles.getVersion();
- mPackagesHash = roles.getPackagesHash();
-
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;
+ if (roleState == null) {
+ scheduleWriteFileLocked();
}
}
- 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);
- }
- }
-
- return roleHolders;
}
/**
@@ -549,11 +461,6 @@
}
}
- @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 00ab973b..7523671 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.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 @@
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 @@
*/
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 3ee8dd7..6306c5c 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.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 @@
}
@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 @@
}
@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 @@
}
}
- /**
- * 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 @@
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 @@
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 @@
}
}
- private void setFullscreen(boolean isFullscreen) {
- mFullscreen = isFullscreen;
- }
-
- private void setImmersive(boolean isImmersive) {
- mImmersive = isImmersive;
- }
-
private int getDisabled1() {
return mDisabled1;
}
@@ -1200,7 +1155,7 @@
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 808d130..eb4a050 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 @@
}
}
+ 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 93ba758..753b42b 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 @@
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 @@
rewriteTextClassificationIcons(result);
} else if (parcelled instanceof ConversationActions) {
rewriteConversationActionsIcons(result);
+ } else if (parcelled instanceof TextSelection) {
+ rewriteTextSelectionIcons(result);
} else {
// do nothing.
}
@@ -1067,10 +1069,32 @@
}
}
- 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 @@
}
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 42f12eb..0772503 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 @@
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 @@
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 @@
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 @@
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 a036bd1..38ae51f 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 @@
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 @@
}
@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 @@
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 @@
}
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 @@
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 @@
+ " }";
}
+ 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 @@
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 072bdd2..988582d 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.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.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 @@
} else {
// Add a new fe resource
FrontendResource newFe = new FrontendResource.Builder(infos[i].handle)
- .type(infos[i].frontendType)
+ .type(infos[i].type)
.exclusiveGroupId(infos[i].exclusiveGroupId)
.build();
addFrontendResource(newFe);
diff --git a/services/core/java/com/android/server/utils/quota/OWNERS b/services/core/java/com/android/server/utils/quota/OWNERS
new file mode 100644
index 0000000..a2943f3
--- /dev/null
+++ b/services/core/java/com/android/server/utils/quota/OWNERS
@@ -0,0 +1,4 @@
+dplotnikov@google.com
+kwekua@google.com
+omakoto@google.com
+yamasani@google.com
diff --git a/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java b/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java
index edbc058..3968723 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 @@
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 @@
*
* @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 b0266d0..fe3b03ab 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 @@
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 @@
/** 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 @@
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 @@
* 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 @@
/** Return the effect that should be played by this vibration. */
@Nullable
- public VibrationEffect getEffect() {
+ public CombinedVibrationEffect getEffect() {
return mEffect;
}
@@ -154,8 +155,8 @@
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 @@
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 @@
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 5f7e47d..0fa4fe1 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 @@
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 @@
}
/**
+ * 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 @@
int intensity = mSettingsController.getCurrentIntensity(usageHint);
int newStrength = intensityToEffectStrength(intensity);
VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
+ int strength = prebaked.getEffectStrength();
+ VibrationEffect fallback = prebaked.getFallbackEffect();
- if (prebaked.getEffectStrength() == newStrength) {
+ 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 @@
return effect.scale(scale.factor);
}
-
-
/** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */
private static int intensityToEffectStrength(int intensity) {
switch (intensity) {
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
new file mode 100644
index 0000000..a4d888b
--- /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 311c73b..53f52e2 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 @@
}
}
+ @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 @@
* {@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 @@
* {@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 9554f2e..f9cc334 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.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 @@
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 @@
pw.print("pendingRemoteAnimationCallingPid=");
pw.println(mPendingRemoteAnimation.getCallingPid());
}
+ if (mPendingRemoteTransition != null) {
+ pw.print(prefix + " pendingRemoteTransition=" + mPendingRemoteTransition);
+ }
if (appTimeTracker != null) {
appTimeTracker.dumpWithHeader(pw, prefix, false);
}
@@ -3244,6 +3249,11 @@
@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 @@
if (options.getAnimationType() == ANIM_REMOTE_ANIMATION) {
mPendingRemoteAnimation = options.getRemoteAnimationAdapter();
}
+ mPendingRemoteTransition = options.getRemoteTransition();
}
void applyOptionsAnimation() {
@@ -4064,6 +4075,7 @@
void clearOptionsAnimation() {
mPendingOptions = null;
mPendingRemoteAnimation = null;
+ mPendingRemoteTransition = null;
}
ActivityOptions getOptions() {
@@ -4078,6 +4090,12 @@
return opts;
}
+ IRemoteTransition takeRemoteTransition() {
+ IRemoteTransition out = mPendingRemoteTransition;
+ mPendingRemoteTransition = null;
+ return out;
+ }
+
boolean allowMoveToFront() {
return mPendingOptions == null || !mPendingOptions.getAvoidMoveToFront();
}
@@ -4773,7 +4791,7 @@
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 @@
mLastReportedConfiguration.setConfiguration(global, override);
}
- boolean hasCompatDisplayInsets() {
- return mCompatDisplayInsets != null;
+ @Nullable
+ CompatDisplayInsets getCompatDisplayInsets() {
+ return mCompatDisplayInsets;
}
/**
@@ -7803,13 +7822,16 @@
/**
* The precomputed insets of the display in each rotation. This is used to make the size
* compatibility mode activity compute the configuration without relying on its current display.
- * This currently only supports fullscreen and freeform windowing mode.
*/
static class CompatDisplayInsets {
+ /** The container width on rotation 0. */
private final int mWidth;
+ /** The container height on rotation 0. */
private final int mHeight;
+ /** Whether the {@link Task} windowingMode represents a floating window*/
final boolean mIsFloating;
-
+ /** Whether the {@link Task} is letterboxed when the unresizable activity is first shown. */
+ final boolean mIsTaskLetterboxed;
/**
* The nonDecorInsets for each rotation. Includes the navigation bar and cutout insets. It
* is used to compute the appBounds.
@@ -7836,27 +7858,24 @@
mNonDecorInsets[rotation] = emptyRect;
mStableInsets[rotation] = emptyRect;
}
+ mIsTaskLetterboxed = false;
return;
}
final Task task = container.getTask();
- if (task != null && task.isTaskLetterboxed()) {
- // For apps in Task letterbox, it should fill the task bounds.
- final Point dimensions = getRotationZeroDimensions(task);
- mWidth = dimensions.x;
- mHeight = dimensions.y;
- } else {
- // If the activity is not floating nor letterboxed, assume it fills the root.
- final RootDisplayArea root = container.getRootDisplayArea();
- if (root == null || root == display) {
- mWidth = display.mBaseDisplayWidth;
- mHeight = display.mBaseDisplayHeight;
- } else {
- final Point dimensions = getRotationZeroDimensions(root);
- mWidth = dimensions.x;
- mHeight = dimensions.y;
- }
- }
+ mIsTaskLetterboxed = task != null && task.isTaskLetterboxed();
+
+ // Store the bounds of the Task for the non-resizable activity to use in size compat
+ // mode so that the activity will not be resized regardless the windowing mode it is
+ // currently in.
+ final WindowContainer filledContainer = task != null ? task : display;
+ final Point dimensions = getRotationZeroDimensions(filledContainer);
+ mWidth = dimensions.x;
+ mHeight = dimensions.y;
+
+ // Bounds of the filled container if it doesn't fill the display.
+ final Rect unfilledContainerBounds =
+ filledContainer.getBounds().equals(display.getBounds()) ? null : new Rect();
final DisplayPolicy policy = display.getDisplayPolicy();
for (int rotation = 0; rotation < 4; rotation++) {
mNonDecorInsets[rotation] = new Rect();
@@ -7869,6 +7888,20 @@
policy.getNonDecorInsetsLw(rotation, dw, dh, cutout, mNonDecorInsets[rotation]);
mStableInsets[rotation].set(mNonDecorInsets[rotation]);
policy.convertNonDecorInsetsToStableInsets(mStableInsets[rotation], rotation);
+
+ if (unfilledContainerBounds == null) {
+ continue;
+ }
+ // The insets is based on the display, but the container may be smaller than the
+ // display, so update the insets to exclude parts that are not intersected with the
+ // container.
+ unfilledContainerBounds.set(filledContainer.getBounds());
+ display.rotateBounds(
+ filledContainer.getConfiguration().windowConfiguration.getRotation(),
+ rotation,
+ unfilledContainerBounds);
+ updateInsetsForBounds(unfilledContainerBounds, dw, dh, mNonDecorInsets[rotation]);
+ updateInsetsForBounds(unfilledContainerBounds, dw, dh, mStableInsets[rotation]);
}
}
@@ -7886,6 +7919,18 @@
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 20625c8..e67210e 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.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 @@
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 @@
}
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 3c06488..b332739 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 @@
.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 599bf37..a68f5575 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 @@
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 d4629d9..9769244 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 @@
// 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 @@
+ " 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 @@
+ " 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 @@
&& 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 @@
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 @@
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 fb005b3..d9bc619 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_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.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.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 @@
// 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 @@
private boolean mDreamingLockscreen;
private boolean mAllowLockscreenWhenOn;
- @VisibleForTesting
- EventReceiverInputConsumer mInputConsumer;
-
private PointerLocationView mPointerLocationView;
/**
@@ -380,6 +365,14 @@
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 @@
break;
case TYPE_NAVIGATION_BAR:
mNavigationBar = win;
+ updateNavBarFadeController();
mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win,
(displayFrames, windowState, inOutFrame) -> {
@@ -1231,6 +1225,7 @@
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 @@
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 @@
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 @@
- 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 @@
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 @@
&& mLastDockedAppearance == dockedAppearance
&& mLastBehavior == behavior
&& mLastFocusIsFullscreen == isFullscreen
- && mLastFocusIsImmersive == isImmersive
&& mLastNonDockedStackBounds.equals(mNonDockedStackBounds)
&& mLastDockedStackBounds.equals(mDockedStackBounds)) {
return false;
@@ -2671,7 +2589,6 @@
mLastDockedAppearance = dockedAppearance;
mLastBehavior = behavior;
mLastFocusIsFullscreen = isFullscreen;
- mLastFocusIsImmersive = isImmersive;
mLastNonDockedStackBounds.set(mNonDockedStackBounds);
mLastDockedStackBounds.set(mDockedStackBounds);
final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds);
@@ -2688,9 +2605,8 @@
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 @@
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 @@
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 c4aaf7c..df5d3ea3 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 @@
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 @@
}
}
// 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 0000000..17d20ae
--- /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 cc02e99..a1e3ac7 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 @@
* 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 @@
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 560547e..a20d924 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 @@
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.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 @@
}
}
- 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 @@
}
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 @@
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 @@
}
}
- 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 ee150c3..94a7ebd 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 @@
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 40e7a8e..0dfa2d8 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 @@
// 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 @@
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 752d6b4..a1461b2 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 @@
* 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 @@
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 @@
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 @@
* @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 @@
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 0000000..30861eb
--- /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 388577c..00b6ceb 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 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.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 @@
// 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 @@
mDisplayId = displayId;
mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
mDisplayContent = service.mRoot.getDisplayContent(displayId);
+ mShouldAttachNavBarToAppDuringTransition =
+ mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition();
}
/**
@@ -439,6 +444,10 @@
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 @@
}
}
+ @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 @@
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 23acbf7..d8b1e18 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 @@
"<<< 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 @@
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 @@
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 1a27b1b..533c82e 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 @@
.setCallsite("ScreenRotationAnimation")
.build();
+ String name = "RotationLayer";
mScreenshotLayer = displayContent.makeOverlay()
- .setName("RotationLayer")
+ .setName(name)
.setBufferSize(mWidth, mHeight)
.setSecure(isSecure)
.setCallsite("ScreenRotationAnimation")
.build();
+ // This is the way to tell the input system to exclude this surface from occlusion
+ // detection since we don't have a window for it. We do this because this window is
+ // generated by the system as well as its content.
+ InputMonitor.setTrustedOverlayInputInfo(mScreenshotLayer, t, displayId, name);
mEnterBlackFrameLayer = displayContent.makeOverlay()
.setName("EnterBlackFrameLayer")
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 637240c..855c8f5 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 @@
/** 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 @@
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 @@
* {@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 @@
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 @@
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 @@
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 @@
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 @@
}
} 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 @@
*/
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 @@
// 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 @@
+ " mode=" + windowingModeToString(getWindowingMode()));
pw.println(" isSleeping=" + shouldSleepActivities());
pw.println(" mBounds=" + getRequestedOverrideBounds());
+ pw.println(" mCreatedByOrganizer=" + mCreatedByOrganizer);
};
boolean printed = false;
@@ -7672,7 +7703,7 @@
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 832fc68..0136c01 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 @@
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 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 @@
// 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 @@
|| 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 @@
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 @@
}
/** 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 @@
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 65247d0..b3e0108 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 @@
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 @@
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 @@
// 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 @@
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 @@
// 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 @@
}
}
+ 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 @@
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 @@
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,16 +480,40 @@
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
public void createRootTask(int displayId, int windowingMode, @Nullable IBinder launchCookie) {
enforceTaskPermission("createRootTask()");
@@ -518,30 +580,76 @@
}
}
- 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 @@
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 ed90cc75..513fa70 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 @@
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 b37e3c4..46aea23 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.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 @@
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 @@
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 @@
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 0fe0afa..5f46ffe 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.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 @@
}
/**
- * @see #requestTransitionIfNeeded(int, int)
+ * @see #requestTransitionIfNeeded(int, int, WindowContainer, IRemoteTransition)
*/
@Nullable
Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type,
@@ -148,9 +150,19 @@
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 @@
*/
@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 @@
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 @@
/** 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 @@
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 c4eb635..52ed278 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 @@
mService.destroyPreservedSurfaceLocked();
+ mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
executeAfterPrepareSurfacesRunnables();
if (DEBUG_WINDOW_TRACE) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index be1f7e1..1509ff6 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 @@
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 124e120..13b9765 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -213,6 +213,7 @@
import android.os.WorkSource;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.MergedConfiguration;
@@ -645,9 +646,14 @@
boolean mSeamlesslyRotated = false;
/**
- * Indicates if this window is behind IME. Only windows behind IME can get insets from IME.
+ * The insets state of sources provided by windows above the current window.
*/
- boolean mBehindIme = false;
+ InsetsState mAboveInsetsState = new InsetsState();
+
+ /**
+ * The insets sources provided by this window.
+ */
+ ArrayMap<Integer, InsetsSource> mProvidedInsetsSources = new ArrayMap<>();
/**
* Surface insets from the previous call to relayout(), used to track
@@ -2097,7 +2103,7 @@
return getDisplayContent().getBounds().equals(getBounds());
}
- private boolean matchesRootDisplayAreaBounds() {
+ boolean matchesRootDisplayAreaBounds() {
RootDisplayArea root = getRootDisplayArea();
if (root == null || root == getDisplayContent()) {
return matchesDisplayBounds();
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 13c6752..dc15b07 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 @@
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 4e1a234..a5311f3 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 @@
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 @@
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 @@
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 @@
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 @@
"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 737bf95..7df9016 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3383,6 +3383,7 @@
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REMOVE_ACTIVE_ADMIN);
enforceUserUnlocked(userHandle);
synchronized (getLockObject()) {
@@ -5143,6 +5144,7 @@
}
final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
Preconditions.checkCallAuthorization(canManageCaCerts(caller));
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_INSTALL_CA_CERT);
final String alias = mInjector.binderWithCleanCallingIdentity(() -> {
String installedAlias = mCertificateMonitor.installCaCert(
@@ -5174,6 +5176,7 @@
}
final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
Preconditions.checkCallAuthorization(canManageCaCerts(caller));
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_UNINSTALL_CA_CERT);
mInjector.binderWithCleanCallingIdentity(() -> {
mCertificateMonitor.uninstallCaCerts(caller.getUserHandle(), aliases);
@@ -5203,6 +5206,7 @@
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
&& (isProfileOwner(caller) || isDeviceOwner(caller)))
|| (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_INSTALL_KEY_PAIR);
final long id = mInjector.binderClearCallingIdentity();
try {
@@ -5260,6 +5264,7 @@
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
&& (isProfileOwner(caller) || isDeviceOwner(caller)))
|| (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REMOVE_KEY_PAIR);
final long id = Binder.clearCallingIdentity();
try {
@@ -6157,6 +6162,7 @@
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_ALWAYS_ON_VPN_PACKAGE);
final int userId = caller.getUserId();
mInjector.binderWithCleanCallingIdentity(() -> {
@@ -6500,6 +6506,8 @@
CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller)
|| isProfileOwnerOfOrganizationOwnedDevice(caller));
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager
+ .OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY);
final int frpManagementAgentUid = getFrpManagementAgentUidOrThrow();
synchronized (getLockObject()) {
@@ -7397,6 +7405,7 @@
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller));
checkAllUsersAreAffiliatedWithDevice();
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REQUEST_BUGREPORT);
if (mBugreportCollectionManager.requestBugreport()) {
DevicePolicyEventLogger
@@ -7506,6 +7515,7 @@
if (parent) {
Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
}
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_CAMERA_DISABLED);
final int userHandle = caller.getUserId();
synchronized (getLockObject()) {
@@ -8640,11 +8650,6 @@
if (isCallerProfileOwnerOrDelegate && isProfileOwnerOfOrganizationOwnedDevice(userId)) {
return true;
}
- //TODO(b/130844684): Temporarily allow profile owner on non-organization-owned devices
- //to read device identifiers.
- if (isCallerProfileOwnerOrDelegate) {
- return true;
- }
return false;
}
@@ -9256,6 +9261,7 @@
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_RESTRICTIONS_PROVIDER);
synchronized (getLockObject()) {
int userHandle = caller.getUserId();
@@ -11431,6 +11437,7 @@
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_MASTER_VOLUME_MUTED);
synchronized (getLockObject()) {
setUserRestriction(who, UserManager.DISALLOW_UNMUTE_DEVICE, on, /* parent */ false);
@@ -12407,6 +12414,7 @@
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
&& (isProfileOwner(caller) || isDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)));
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_PERMISSION_POLICY);
synchronized (getLockObject()) {
DevicePolicyData userPolicy = getUserData(caller.getUserId());
@@ -12442,6 +12450,7 @@
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
&& (isProfileOwner(caller) || isDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)));
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_PERMISSION_GRANT_STATE);
synchronized (getLockObject()) {
long ident = mInjector.binderClearCallingIdentity();
@@ -14418,6 +14427,7 @@
final CallerIdentity caller = getCallerIdentity(admin);
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_CLEAR_APPLICATION_USER_DATA);
long ident = mInjector.binderClearCallingIdentity();
try {
@@ -14449,6 +14459,7 @@
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOGOUT_ENABLED);
synchronized (getLockObject()) {
ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
@@ -14816,6 +14827,8 @@
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_OVERRIDE_APNS_ENABLED);
+
setOverrideApnsEnabledUnchecked(enabled);
}
@@ -14917,6 +14930,7 @@
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_GLOBAL_PRIVATE_DNS);
switch (mode) {
case PRIVATE_DNS_MODE_OPPORTUNISTIC:
@@ -14989,6 +15003,7 @@
final CallerIdentity caller = getCallerIdentity(admin);
Preconditions.checkCallAuthorization(isDeviceOwner(caller)
|| isProfileOwnerOfOrganizationOwnedDevice(caller));
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_INSTALL_SYSTEM_UPDATE);
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.INSTALL_SYSTEM_UPDATE)
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 94df185..57b3e8d 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.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 @@
// Grants default permissions and defines roles
t.traceBegin("StartRoleManagerService");
mSystemServiceManager.startService(new RoleManagerService(
- mSystemContext, new LegacyRoleResolutionPolicy(mSystemContext)));
+ mSystemContext, new LegacyRoleStateProviderImpl(mSystemContext)));
t.traceEnd();
// We need to always start this service, regardless of whether the
diff --git a/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java b/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java
index e4daddc..0a85f7f 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 @@
}
@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 @@
}
/**
- * Reads all files in directory and returns a map with file names as keys and parsed file
- * contents as values.
- */
- @WorkerThread
- @Nullable
- Map<String, T> readAll() {
- File[] files = mRootDir.listFiles(File::isFile);
- if (files == null) {
- return null;
- }
-
- Map<String, T> results = new ArrayMap<>();
- for (File file : files) {
- T result = parseFile(file);
- if (result != null) {
- results.put(file.getName(), result);
- }
- }
- return results;
- }
-
- /**
* Schedules the specified data to be flushed to a file in the future. Subsequent
* calls for the same file before the flush occurs will replace the previous data but will not
* reset when the flush will occur. All unique files will be flushed at the same time.
diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java
index 28e3d4b..6faeb80 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 @@
* 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 @@
* 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 @@
}
@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 @@
}
@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 @@
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 @@
}
}
- @MainThread
private synchronized void updateConversationsInMemory(
@NonNull ConversationInfo conversationInfo) {
mConversationInfoMap.put(conversationInfo.getShortcutId(), conversationInfo);
@@ -273,12 +284,15 @@
/** Schedules a dump of all conversations onto disk, overwriting existing values. */
@MainThread
- private synchronized void scheduleUpdateConversationsOnDisk() {
+ private void scheduleUpdateConversationsOnDisk() {
ConversationInfosProtoDiskReadWriter conversationInfosProtoDiskReadWriter =
getConversationInfosProtoDiskReadWriter();
if (conversationInfosProtoDiskReadWriter != null) {
- conversationInfosProtoDiskReadWriter.scheduleConversationsSave(
- new ArrayList<>(mConversationInfoMap.values()));
+ List<ConversationInfo> conversations;
+ synchronized (this) {
+ conversations = new ArrayList<>(mConversationInfoMap.values());
+ }
+ conversationInfosProtoDiskReadWriter.scheduleConversationsSave(conversations);
}
}
diff --git a/services/searchui/OWNERS b/services/searchui/OWNERS
new file mode 100644
index 0000000..92835c2
--- /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 fbde1d2..e4b650c 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 f4c6918..e99113d 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 @@
// 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 0000000..3710396
--- /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 dcbf8c0..4effa4d 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.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 @@
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 1a65894..c4c9173 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 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 @@
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 @@
@Mock
private PackageManagerInternal mPackageManagerInternal;
@Mock
+ private PowerAllowlistInternal mPowerAllowlistInternal;
+ @Mock
private UsageStatsManagerInternal mUsageStatsManager;
private JobStore mJobStore;
@@ -173,6 +177,8 @@
.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
/**
+ * 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 @@
}
/**
+ * Tests that Timers properly track sessions when an app is added and removed from the temp
+ * allowlist.
+ */
+ @Test
+ public void testEJTimerTracking_TempAllowlisting() {
+ setDischarging();
+ setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+ final long gracePeriodMs = 15 * SECOND_IN_MILLIS;
+ setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, gracePeriodMs);
+ Handler handler = mQuotaController.getHandler();
+ spyOn(handler);
+
+ JobStatus job1 = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting", 1);
+ JobStatus job2 = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting", 2);
+ JobStatus job3 = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting", 3);
+ JobStatus job4 = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting", 4);
+ JobStatus job5 = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting", 5);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(job1, null);
+ }
+ assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ List<TimingSession> expected = new ArrayList<>();
+
+ long start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(job1);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(job1, job1, true);
+ }
+ expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+ assertEquals(expected,
+ mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(SECOND_IN_MILLIS);
+
+ // Job starts after app is added to temp allowlist and stops before removal.
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mTempAllowlistListener.onAppAdded(mSourceUid);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(job2, null);
+ mQuotaController.prepareForExecutionLocked(job2);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(job2, null, false);
+ }
+ assertEquals(expected,
+ mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ // Job starts after app is added to temp allowlist and stops after removal,
+ // before grace period ends.
+ mTempAllowlistListener.onAppAdded(mSourceUid);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(job3, null);
+ mQuotaController.prepareForExecutionLocked(job3);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mTempAllowlistListener.onAppRemoved(mSourceUid);
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ long elapsedGracePeriodMs = 2 * SECOND_IN_MILLIS;
+ advanceElapsedClock(elapsedGracePeriodMs);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(job3, null, false);
+ }
+ assertEquals(expected,
+ mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(SECOND_IN_MILLIS);
+ elapsedGracePeriodMs += SECOND_IN_MILLIS;
+
+ // Job starts during grace period and ends after grace period ends
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(job4, null);
+ mQuotaController.prepareForExecutionLocked(job4);
+ }
+ final long remainingGracePeriod = gracePeriodMs - elapsedGracePeriodMs;
+ start = JobSchedulerService.sElapsedRealtimeClock.millis() + remainingGracePeriod;
+ advanceElapsedClock(remainingGracePeriod);
+ // Wait for handler to update Timer
+ // Can't directly evaluate the message because for some reason, the captured message returns
+ // the wrong 'what' even though the correct message goes to the handler and the correct
+ // path executes.
+ verify(handler, timeout(gracePeriodMs + 5 * SECOND_IN_MILLIS)).handleMessage(any());
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(job4, job4, true);
+ }
+ assertEquals(expected,
+ mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ // Job starts and runs completely after temp allowlist grace period.
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(job5, null);
+ mQuotaController.prepareForExecutionLocked(job5);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(job5, job5, true);
+ }
+ expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+ assertEquals(expected,
+ mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ /**
* Tests that expedited jobs aren't stopped when an app runs out of quota.
*/
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/quota/OWNERS b/services/tests/mockingservicestests/src/com/android/server/utils/quota/OWNERS
new file mode 100644
index 0000000..2e9e625
--- /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 726536d..0a35db5 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.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 androidx.test.InstrumentationRegistry;
+import com.android.server.vibrator.FakeVibratorControllerProvider;
import com.android.server.vibrator.VibratorController;
import org.junit.After;
@@ -81,7 +78,7 @@
@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 @@
@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 @@
}
@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 @@
@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 @@
@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 @@
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 @@
"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 32ca7b5..5c1e021 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 @@
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.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 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.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 @@
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 @@
@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 @@
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 @@
@Override
VibratorController createVibratorController(
VibratorController.OnVibrationCompleteListener listener) {
- return new VibratorController(0, listener, mNativeWrapperMock);
+ return mVibratorProvider.newVibratorController(VIBRATOR_ID, listener);
}
@Override
@@ -203,25 +193,23 @@
@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 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 @@
}
@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 @@
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 @@
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 @@
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);
+ vibrateAndWait(service, effect, ALARM_ATTRS);
- // 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());
-
- 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 @@
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());
+
+ // VibrationThread will start this vibration async, so wait before checking it never played.
+ Thread.sleep(10);
+ assertTrue(mVibratorProvider.getEffects().isEmpty());
}
@Test
- public void vibrate_withOneShotAndNativeCallbackTriggered_finishesVibration() {
+ public void vibrate_withNativeCallbackTriggered_finishesVibration() throws Exception {
+ mVibratorProvider.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
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));
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
- InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
- inOrderVerifier.verify(mNativeWrapperMock).off();
- inOrderVerifier.verify(mNativeWrapperMock).on(eq(100L), gt(0L));
- inOrderVerifier.verify(mNativeWrapperMock).off();
- }
-
- @Test
- public void vibrate_withPrebakedAndNativeCallbackTriggered_finishesVibration() {
- when(mNativeWrapperMock.getSupportedEffects())
- .thenReturn(new int[]{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();
- }
-
- @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);
-
- VibrationEffect effect = VibrationEffect.createWaveform(new long[]{1, 3, 1, 2}, -1);
- vibrate(service, effect);
-
- // 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();
- }
-
- @Test
- public void vibrate_withComposedAndNativeCallbackTriggered_finishesVibration() {
- mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- 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();
- }
-
- @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 triggering callbacks.
+ Thread.sleep(10);
assertTrue(service.isVibrating());
- Mockito.clearInvocations(mNativeWrapperMock);
- service.cancelVibrate(service);
+ // Trigger callbacks from controller.
+ mTestLooper.moveTimeForward(50);
+ mTestLooper.dispatchAll();
+
+ // VibrationThread needs some time to react to native callbacks and stop the vibrator.
+ Thread.sleep(10);
assertFalse(service.isVibrating());
- verify(mNativeWrapperMock).off();
}
@Test
- public void cancelVibrate_withDeviceNotVibrating_ignoresCall() {
+ public void cancelVibrate_withDeviceVibrating_callsOff() throws Exception {
VibratorService service = createService();
- Mockito.clearInvocations(mNativeWrapperMock);
+
+ vibrate(service, VibrationEffect.createOneShot(100, 100), ALARM_ATTRS);
+
+ // VibrationThread will start this vibration async, so wait before checking.
+ Thread.sleep(10);
+ assertTrue(service.isVibrating());
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 @@
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 @@
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 @@
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);
+ vibrate(service, VibrationEffect.get(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());
+ 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 @@
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 @@
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 74c6a7e..784718b 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.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.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 @@
// 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 @@
/* 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 @@
}
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 009cb0b..6366155 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 @@
@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 @@
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 a3f0f6b..97daea3 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 @@
// 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 @@
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 @@
// 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 340a1d9..bb2b1c2 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.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 @@
@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 @@
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 600f681..cc4541b 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 @@
@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 @@
// 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 @@
// 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 @@
@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 @@
}
}
- private static class TestClientMonitor extends BaseClientMonitor<Object> {
+ private static class TestClientMonitor extends HalClientMonitor<Object> {
private boolean mUnableToStart;
private boolean mStarted;
@@ -223,7 +223,6 @@
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 ffd4378..04a7122 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 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 @@
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 99aab5c..392535e 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 @@
}
@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 175c4da..d149880 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 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 @@
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 b2aeb33..61cc8e6 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 @@
}
@Test
+ public void getAuthenticatorId_doesNotCrashWhenIdNotFound() {
+ assertEquals(0, mFingerprint21.getAuthenticatorId(0 /* sensorId */, 111 /* userId */));
+ waitForIdle();
+ }
+
+ @Test
public void halServiceDied_resetsScheduler() {
// It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke
// serviceDied directly.
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
new file mode 100644
index 0000000..0cf0af3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.graphics.fonts;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Map;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class UpdatableFontDirTest {
+
+ /**
+ * A {@link UpdatableFontDir.FontFileParser} for testing. Instead of using real font files,
+ * this test uses fake font files. A fake font file has its version as its file content.
+ */
+ private static class FakeFontFileParser implements UpdatableFontDir.FontFileParser {
+ @Override
+ public long getVersion(File file) throws IOException {
+ return Long.parseLong(FileUtils.readTextFile(file, 100, ""));
+ }
+ }
+
+ private File mCacheDir;
+ private File mUpdatableFontFilesDir;
+
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ @Before
+ public void setUp() {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mCacheDir = new File(context.getCacheDir(), "UpdatableFontDirTest");
+ FileUtils.deleteContentsAndDir(mCacheDir);
+ mCacheDir.mkdirs();
+ mUpdatableFontFilesDir = new File(mCacheDir, "updatable_fonts");
+ mUpdatableFontFilesDir.mkdir();
+ }
+
+ @After
+ public void tearDown() {
+ FileUtils.deleteContentsAndDir(mCacheDir);
+ }
+
+ @Test
+ public void construct() throws Exception {
+ FakeFontFileParser parser = new FakeFontFileParser();
+ UpdatableFontDir dirForPreparation = new UpdatableFontDir(mUpdatableFontFilesDir, parser);
+ installFontFile(dirForPreparation, "foo.ttf", "1");
+ installFontFile(dirForPreparation, "bar.ttf", "2");
+ installFontFile(dirForPreparation, "foo.ttf", "3");
+ installFontFile(dirForPreparation, "bar.ttf", "4");
+ // Four font dirs are created.
+ assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
+
+ UpdatableFontDir dir = new UpdatableFontDir(mUpdatableFontFilesDir, parser);
+ assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
+ assertThat(parser.getVersion(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(3);
+ assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
+ assertThat(parser.getVersion(dir.getFontFileMap().get("bar.ttf"))).isEqualTo(4);
+ // Outdated font dir should be deleted.
+ assertThat(mUpdatableFontFilesDir.list()).hasLength(2);
+ }
+
+ @Test
+ public void construct_empty() {
+ FakeFontFileParser parser = new FakeFontFileParser();
+ UpdatableFontDir dir = new UpdatableFontDir(mUpdatableFontFilesDir, parser);
+ assertThat(dir.getFontFileMap()).isEmpty();
+ }
+
+ @Test
+ public void installFontFile() throws Exception {
+ FakeFontFileParser parser = new FakeFontFileParser();
+ UpdatableFontDir dir = new UpdatableFontDir(mUpdatableFontFilesDir, parser);
+
+ installFontFile(dir, "test.ttf", "1");
+ assertThat(dir.getFontFileMap()).containsKey("test.ttf");
+ assertThat(parser.getVersion(dir.getFontFileMap().get("test.ttf"))).isEqualTo(1);
+ }
+
+ @Test
+ public void installFontFile_upgrade() throws Exception {
+ FakeFontFileParser parser = new FakeFontFileParser();
+ UpdatableFontDir dir = new UpdatableFontDir(mUpdatableFontFilesDir, parser);
+
+ installFontFile(dir, "test.ttf", "1");
+ Map<String, File> mapBeforeUpgrade = dir.getFontFileMap();
+ installFontFile(dir, "test.ttf", "2");
+ assertThat(dir.getFontFileMap()).containsKey("test.ttf");
+ assertThat(parser.getVersion(dir.getFontFileMap().get("test.ttf"))).isEqualTo(2);
+ assertThat(mapBeforeUpgrade).containsKey("test.ttf");
+ assertWithMessage("Older fonts should not be deleted until next loadFontFileMap")
+ .that(parser.getVersion(mapBeforeUpgrade.get("test.ttf"))).isEqualTo(1);
+ }
+
+ @Test
+ public void installFontFile_downgrade() throws Exception {
+ FakeFontFileParser parser = new FakeFontFileParser();
+ UpdatableFontDir dir = new UpdatableFontDir(mUpdatableFontFilesDir, parser);
+
+ installFontFile(dir, "test.ttf", "2");
+ installFontFile(dir, "test.ttf", "1");
+ assertThat(dir.getFontFileMap()).containsKey("test.ttf");
+ assertWithMessage("Font should not be downgraded to an older version")
+ .that(parser.getVersion(dir.getFontFileMap().get("test.ttf"))).isEqualTo(2);
+ }
+
+ @Test
+ public void installFontFile_multiple() throws Exception {
+ FakeFontFileParser parser = new FakeFontFileParser();
+ UpdatableFontDir dir = new UpdatableFontDir(mUpdatableFontFilesDir, parser);
+
+ installFontFile(dir, "foo.ttf", "1");
+ installFontFile(dir, "bar.ttf", "2");
+ assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
+ assertThat(parser.getVersion(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
+ assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
+ assertThat(parser.getVersion(dir.getFontFileMap().get("bar.ttf"))).isEqualTo(2);
+ }
+
+ private void installFontFile(UpdatableFontDir dir, String name, String content)
+ throws IOException {
+ File file = File.createTempFile(name, "", mCacheDir);
+ FileUtils.stringToFile(file, content);
+ try (FileInputStream in = new FileInputStream(file)) {
+ dir.installFontFile(name, in.getFD());
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
index b7d9a56..a7b32ac 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.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 @@
assertTrue("Job did not start after scheduling", awaitJobStart(DEFAULT_WAIT_TIMEOUT));
}
+ @FlakyTest
@Test
public void testPowerExemption() throws Exception {
scheduleAndAssertJobStarted();
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 5f65440..a5039fb 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 @@
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 @@
}
@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 3858370..aadab6e 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.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.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 @@
}
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 @@
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 7c65dc0..b40d59c 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 0000000..72c40ea
--- /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 0000000..f562c16
--- /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 ac93ff6..28d313b 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.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 @@
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 @@
@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 @@
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 1a4ac07..1e6ef91 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 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 @@
@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 @@
@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 @@
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 @@
}
@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 @@
}
@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 @@
@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 @@
@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 1e112da..d867987 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -76,26 +76,25 @@
@Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
- // TODO(b/131311651): replace with a FakeVibrator instead.
- @Mock private Vibrator mVibratorMock;
@Mock private VibrationSettings.OnVibratorSettingsChanged mListenerMock;
@Mock private PowerManagerInternal mPowerManagerInternalMock;
private TestLooper mTestLooper;
private ContextWrapper mContextSpy;
private AudioManager mAudioManager;
+ private FakeVibrator mFakeVibrator;
private VibrationSettings mVibrationSettings;
private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
@Before
public void setUp() throws Exception {
mTestLooper = new TestLooper();
+ mFakeVibrator = new FakeVibrator();
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
- when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibratorMock);
- when(mVibratorMock.hasVibrator()).thenReturn(true);
+ when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mFakeVibrator);
doAnswer(invocation -> {
mRegisteredPowerModeListener = invocation.getArgument(0);
return null;
@@ -305,12 +304,9 @@
@Test
public void getDefaultIntensity_returnsIntensityFromVibratorService() {
- when(mVibratorMock.getDefaultHapticFeedbackIntensity())
- .thenReturn(Vibrator.VIBRATION_INTENSITY_HIGH);
- when(mVibratorMock.getDefaultNotificationVibrationIntensity())
- .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM);
- when(mVibratorMock.getDefaultRingVibrationIntensity())
- .thenReturn(Vibrator.VIBRATION_INTENSITY_LOW);
+ mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
+ mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
+ mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_OFF);
@@ -336,12 +332,9 @@
@Test
public void getCurrentIntensity_returnsIntensityFromSettings() {
- when(mVibratorMock.getDefaultHapticFeedbackIntensity())
- .thenReturn(Vibrator.VIBRATION_INTENSITY_OFF);
- when(mVibratorMock.getDefaultNotificationVibrationIntensity())
- .thenReturn(Vibrator.VIBRATION_INTENSITY_OFF);
- when(mVibratorMock.getDefaultRingVibrationIntensity())
- .thenReturn(Vibrator.VIBRATION_INTENSITY_OFF);
+ mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_OFF);
+ mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_OFF);
+ mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Vibrator.VIBRATION_INTENSITY_HIGH);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
new file mode 100644
index 0000000..bee7392
--- /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 4d2a478..8c744c9 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.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 @@
}
@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 610edc0..6f584ee 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.WindowConfiguration;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.DestroyActivityItem;
import android.app.servertransaction.PauseActivityItem;
import android.content.ComponentName;
import android.content.Intent;
@@ -1449,6 +1450,19 @@
}
@Test
+ public void testRemoveImmediately() throws RemoteException {
+ final ActivityRecord activity = createActivityWithTask();
+ final WindowProcessController wpc = activity.app;
+ activity.getTask().removeImmediately("test");
+
+ verify(mAtm.getLifecycleManager()).scheduleTransaction(any(), eq(activity.appToken),
+ isA(DestroyActivityItem.class));
+ assertNull(activity.app);
+ assertEquals(DESTROYED, activity.getState());
+ assertFalse(wpc.hasActivities());
+ }
+
+ @Test
public void testRemoveFromHistory() {
final ActivityRecord activity = createActivityWithTask();
final Task rootTask = activity.getRootTask();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 9527625..11be74d 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 @@
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 0f03f68..06784af 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 @@
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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 79b2da1..22ee7e4 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.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_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.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 @@
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 @@
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 @@
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 61911b3..e6f24da 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_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 @@
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 e0fd379..bf3ed69 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 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 @@
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 @@
@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 2766438..2107ab1e 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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 4dce451..409bad4 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 @@
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.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.util.SparseBooleanArray;
import android.view.IRecentsAnimationRunner;
import android.view.SurfaceControl;
+import android.window.TaskSnapshot;
import androidx.test.filters.SmallTest;
@@ -99,6 +100,7 @@
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 @@
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 @@
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 e190248..eb44476 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.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 @@
// 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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 1607f01..a1f89ec 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 @@
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 fe7bdd8..d8be2c1 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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 @@
.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 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
assertTrue(task2.isOrganized());
}
+ @Test
+ public void testAppearDeferThenInfoChange() {
+ final ITaskOrganizer organizer = registerMockOrganizer();
+ final Task stack = createStack();
+
+ // Assume layout defer
+ mWm.mWindowPlacerLocked.deferLayout();
+
+ final Task task = createTask(stack);
+ final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+
+ stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
+ waitUntilHandlersIdle();
+
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+ assertEquals(1, pendingEvents.size());
+ assertEquals(PendingTaskEvent.EVENT_APPEARED, pendingEvents.get(0).mEventType);
+ assertEquals("TestDescription",
+ pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel());
+ }
+
+ @Test
+ public void testAppearDeferThenVanish() {
+ final ITaskOrganizer organizer = registerMockOrganizer();
+ final Task stack = createStack();
+
+ // Assume layout defer
+ mWm.mWindowPlacerLocked.deferLayout();
+
+ final Task task = createTask(stack);
+
+ stack.removeImmediately();
+ waitUntilHandlersIdle();
+
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+ assertEquals(0, pendingEvents.size());
+ }
+
+ @Test
+ public void testInfoChangeDeferMultiple() {
+ final ITaskOrganizer organizer = registerMockOrganizer();
+ final Task stack = createStack();
+ final Task task = createTask(stack);
+ final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+
+ // Assume layout defer
+ mWm.mWindowPlacerLocked.deferLayout();
+
+ stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
+ waitUntilHandlersIdle();
+
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+ assertEquals(1, pendingEvents.size());
+ assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType);
+ assertEquals("TestDescription",
+ pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel());
+
+ record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription2"));
+ waitUntilHandlersIdle();
+
+ pendingEvents = getTaskPendingEvent(stack);
+ assertEquals(1, pendingEvents.size());
+ assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType);
+ assertEquals("TestDescription2",
+ pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel());
+ }
+
+ @Test
+ public void testInfoChangDeferThenVanish() {
+ final ITaskOrganizer organizer = registerMockOrganizer();
+ final Task stack = createStack();
+ final Task task = createTask(stack);
+ final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+
+ // Assume layout defer
+ mWm.mWindowPlacerLocked.deferLayout();
+
+ stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
+
+ stack.removeImmediately();
+ waitUntilHandlersIdle();
+
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+ assertEquals(1, pendingEvents.size());
+ assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
+ assertEquals("TestDescription",
+ pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel());
+ }
+
+ @Test
+ public void testVanishDeferThenInfoChange() {
+ final ITaskOrganizer organizer = registerMockOrganizer();
+ final Task stack = createStack();
+ final Task task = createTask(stack);
+ final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+
+ // Assume layout defer
+ mWm.mWindowPlacerLocked.deferLayout();
+
+ stack.removeImmediately();
+ stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ waitUntilHandlersIdle();
+
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+ assertEquals(1, pendingEvents.size());
+ assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
+ }
+
+ @Test
+ public void testVanishDeferThenBackOnRoot() {
+ final ITaskOrganizer organizer = registerMockOrganizer();
+ final Task stack = createStack();
+ final Task task = createTask(stack);
+ final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+
+ // Assume layout defer
+ mWm.mWindowPlacerLocked.deferLayout();
+
+ stack.removeImmediately();
+ mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(record.token);
+ waitUntilHandlersIdle();
+
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+ assertEquals(1, pendingEvents.size());
+ assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
+ }
+
+ private ArrayList<PendingTaskEvent> getTaskPendingEvent(Task task) {
+ ArrayList<PendingTaskEvent> total =
+ mWm.mAtmService.mTaskOrganizerController.getPendingEventList();
+ ArrayList<PendingTaskEvent> result = new ArrayList();
+
+ for (int i = 0; i < total.size(); i++) {
+ PendingTaskEvent entry = total.get(i);
+ if (entry.mTask.mTaskId == task.mTaskId) {
+ result.add(entry);
+ }
+ }
+
+ return result;
+ }
+
/**
* Verifies that task vanished is called for a specific task.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 811a146..c82ba99 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_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 @@
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 e2aabe6..84c6e7b 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 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 @@
}
}
}
+
+ @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 b1f6f80..5b1074f 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.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.server.infra.AbstractPerUserSystemService;
import java.util.ArrayList;
+import java.util.List;
final class TranslationManagerServiceImpl extends
AbstractPerUserSystemService<TranslationManagerServiceImpl, TranslationManagerService> {
@@ -122,4 +125,11 @@
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 fd462c2..bfb159f 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 com.android.internal.annotations.VisibleForTesting;
import java.io.IOException;
+import java.util.Arrays;
import java.util.List;
public class IntervalStats {
@@ -459,13 +460,14 @@
*/
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 @@
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 @@
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 @@
}
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 @@
*/
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 @@
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 @@
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 @@
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 @@
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 @@
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 f19abbb..272daf4 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 @@
* 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 @@
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 52b0afb..a0a3909 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 @@
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 9d48955..a4f5249 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.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 @@
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 f1bea67..717040a 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -18,6 +18,7 @@
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 @@
*/
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 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 b1ccb53..f86f9d5 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -147,6 +147,7 @@
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 @@
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 @@
}
@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 @@
}
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 @@
}
}
+ 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 52210a5..feb2ca5 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 @@
}
/**
+ * 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 fb54179..92264be 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -118,6 +118,9 @@
void sendCallEvent(String callId, String event, in Bundle extras, in Session.Info sessionInfo);
+ void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
+ in Session.Info sessionInfo);
+
void onExtrasChanged(String callId, in Bundle extras, in Session.Info sessionInfo);
void startRtt(String callId, in ParcelFileDescriptor fromInCall,
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index d012971..f6d18fc 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -18,10 +18,7 @@
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 @@
* 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 @@
* 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 @@
* 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 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 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 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 @@
BAND_18,
BAND_20,
BAND_25,
+ BAND_26,
BAND_28,
BAND_29,
BAND_30,
@@ -398,9 +776,11 @@
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 @@
BAND_93,
BAND_94,
BAND_95,
+ BAND_96,
BAND_257,
BAND_258,
BAND_260,
@@ -464,7 +845,8 @@
value = {
FREQUENCY_RANGE_GROUP_UNKNOWN,
FREQUENCY_RANGE_GROUP_1,
- FREQUENCY_RANGE_GROUP_2})
+ FREQUENCY_RANGE_GROUP_2
+ })
public @interface FrequencyRangeGroup {}
/**
@@ -489,6 +871,7 @@
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 @@
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 @@
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 @@
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 7661a32..f29f3bd 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_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 @@
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 @@
}
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 94fb812..daa3d1f0 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4643,6 +4643,13 @@
*/
public static final String KEY_USE_ACS_FOR_RCS_BOOL = "use_acs_for_rcs_bool";
+ /**
+ * Indicates temporarily unmetered mobile data is supported by the carrier.
+ * @hide
+ */
+ public static final String KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL =
+ "network_temp_not_metered_supported_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -5193,6 +5200,7 @@
sDefaults.putString(KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING, "");
sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false);
sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false);
+ sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, false);
}
/**
@@ -5211,9 +5219,25 @@
public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT =
KEY_PREFIX + "hotspot_maximum_client_count";
+ /**
+ * This configuration is intended to be a narrow exception for provisioning
+ * {@link android.net.wifi.WifiNetworkSuggestion} of widely-known carrier networks that do
+ * not support using randomized MAC address.
+ * Carrier provisioned {@link android.net.wifi.WifiNetworkSuggestion} with SSIDs included
+ * in this list will have MAC randomization disabled.
+ *
+ * Note: the SSIDs in the list are expected to be interpreted as is - do not add double
+ * quotes to the SSIDs.
+ */
+ public static final String KEY_SUGGESTION_SSID_LIST_WITH_MAC_RANDOMIZATION_DISABLED =
+ KEY_PREFIX + "suggestion_ssid_list_with_mac_randomization_disabled";
+
private static PersistableBundle getDefaults() {
PersistableBundle defaults = new PersistableBundle();
defaults.putInt(KEY_HOTSPOT_MAX_CLIENT_COUNT, 0);
+ defaults.putStringArray(KEY_SUGGESTION_SSID_LIST_WITH_MAC_RANDOMIZATION_DISABLED,
+ new String[0]);
+
return defaults;
}
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index 95c69ba..b359ebe 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -19,7 +19,6 @@
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 @@
/** 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 @@
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 @@
private int mFrequencyRange;
/**
- * The absolute radio frequency channel number, {@link #CHANNEL_NUMBER_UNKNOWN} if unknown.
+ * The frequency of Downlink.
*/
- private int mChannelNumber;
+ 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 mUplinkChannelNumber;
/**
* A list of data calls mapped to this physical channel. An empty list means the physical
@@ -98,6 +129,11 @@
*/
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 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 @@
}
/**
- * @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 @@
}
/**
- * @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 @@
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 @@
* @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 @@
}
}
+ 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 @@
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 @@
.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 @@
* @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 dedb1af..f110dae 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -2111,4 +2111,23 @@
}
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 0000000..2a16402
--- /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 8a472ad..0059ad6 100644
--- a/telephony/java/android/telephony/SignalThresholdInfo.java
+++ b/telephony/java/android/telephony/SignalThresholdInfo.java
@@ -28,13 +28,11 @@
/**
* Defines the threshold value of the signal strength.
- * @hide
*/
public final class SignalThresholdInfo implements Parcelable {
/**
* Unknown signal measurement type.
- * @hide
*/
public static final int SIGNAL_MEASUREMENT_TYPE_UNKNOWN = 0;
@@ -44,7 +42,6 @@
* Used RAN: {@link AccessNetworkConstants.AccessNetworkType#GERAN},
* {@link AccessNetworkConstants.AccessNetworkType#CDMA2000}
* Reference: 3GPP TS 27.007 section 8.5.
- * @hide
*/
public static final int SIGNAL_MEASUREMENT_TYPE_RSSI = 1;
@@ -53,7 +50,6 @@
* Range: -120 dBm to -25 dBm;
* Used RAN: {@link AccessNetworkConstants.AccessNetworkType#UTRAN}
* Reference: 3GPP TS 25.123, section 9.1.1.1
- * @hide
*/
public static final int SIGNAL_MEASUREMENT_TYPE_RSCP = 2;
@@ -62,7 +58,6 @@
* Range: -140 dBm to -44 dBm;
* Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
* Reference: 3GPP TS 36.133 9.1.4
- * @hide
*/
public static final int SIGNAL_MEASUREMENT_TYPE_RSRP = 3;
@@ -71,7 +66,6 @@
* Range: -34 dB to 3 dB;
* Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
* Reference: 3GPP TS 36.133 9.1.7
- * @hide
*/
public static final int SIGNAL_MEASUREMENT_TYPE_RSRQ = 4;
@@ -79,7 +73,6 @@
* Reference Signal Signal to Noise Ratio
* Range: -20 dB to 30 dB;
* Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
- * @hide
*/
public static final int SIGNAL_MEASUREMENT_TYPE_RSSNR = 5;
@@ -88,7 +81,6 @@
* Range: -140 dBm to -44 dBm.
* Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
* Reference: 3GPP TS 38.215.
- * @hide
*/
public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRP = 6;
@@ -97,7 +89,6 @@
* Range: -43 dB to 20 dB.
* Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
* Reference: 3GPP TS 38.133 section 10.1.11.1.
- * @hide
*/
public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRQ = 7;
@@ -106,7 +97,6 @@
* Range: -23 dB to 40 dB
* Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
* Reference: 3GPP TS 38.215 section 5.1.*, 3GPP TS 38.133 section 10.1.16.1.
- * @hide
*/
public static final int SIGNAL_MEASUREMENT_TYPE_SSSINR = 8;
@@ -290,6 +280,20 @@
public static final int SIGNAL_SSSINR_MAX_VALUE = 40;
/**
+ * The minimum number of thresholds allowed in each SignalThresholdInfo.
+ *
+ * @hide
+ */
+ public static final int MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED = 1;
+
+ /**
+ * The maximum number of thresholds allowed in each SignalThresholdInfo.
+ *
+ * @hide
+ */
+ public static final int MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED = 4;
+
+ /**
* Constructor
*
* @param ran Radio Access Network type
@@ -316,8 +320,6 @@
/**
* Builder class to create {@link SignalThresholdInfo} objects.
- *
- * @hide
*/
public static final class Builder {
private int mRan = AccessNetworkConstants.AccessNetworkType.UNKNOWN;
@@ -378,20 +380,57 @@
}
/**
- * Set the signal threshold values of the corresponding signal measurement type.
+ * Set the signal strength thresholds of the corresponding signal measurement type.
*
- * The range and unit must reference specific SignalMeasurementType.
+ * The range and unit must reference specific SignalMeasurementType. The length of the
+ * thresholds should between the numbers return from
+ * {@link #getMinimumNumberOfThresholdsAllowed()} and
+ * {@link #getMaximumNumberOfThresholdsAllowed()}. An IllegalArgumentException will throw
+ * otherwise.
*
* @param thresholds array of integer as the signal threshold values
* @return the builder to facilitate the chaining
+ *
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSSI
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSCP
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSRP
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSRQ
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSSNR
+ * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRP
+ * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRQ
+ * @see #SIGNAL_MEASUREMENT_TYPE_SSSINR
+ * @see #getThresholds() for more details on signal strength thresholds
*/
public @NonNull Builder setThresholds(@NonNull int[] thresholds) {
Objects.requireNonNull(thresholds, "thresholds must not be null");
+ if (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
+ || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED) {
+ throw new IllegalArgumentException(
+ "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
+ + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED);
+ }
+ mThresholds = thresholds.clone();
+ Arrays.sort(mThresholds);
+ return this;
+ }
+
+ /**
+ * Set the signal strength thresholds for the corresponding signal measurement type without
+ * the length limitation.
+ *
+ * @param thresholds array of integer as the signal threshold values
+ * @return the builder to facilitate the chaining
+ *
+ * @hide
+ */
+ public @NonNull Builder setThresholdsUnlimited(@NonNull int[] thresholds) {
+ Objects.requireNonNull(thresholds, "thresholds must not be null");
mThresholds = thresholds.clone();
Arrays.sort(mThresholds);
return this;
}
+
/**
* Set if the modem should trigger the report based on the criteria.
*
@@ -423,8 +462,6 @@
* Get the radio access network type.
*
* @return radio access network type
- *
- * @hide
*/
public @AccessNetworkConstants.RadioAccessNetworkType int getRadioAccessNetworkType() {
return mRan;
@@ -434,8 +471,6 @@
* Get the signal measurement type.
*
* @return the SignalMeasurementType value
- *
- * @hide
*/
public @SignalMeasurementType int getSignalMeasurementType() {
return mSignalMeasurementType;
@@ -457,16 +492,47 @@
}
/**
- * Get the signal threshold values.
+ * Get the signal strength thresholds.
+ *
+ * Signal strength thresholds are a list of integer used for suggesting signal level and signal
+ * reporting criteria. The range and unit must reference specific SignalMeasurementType.
+ *
+ * Please refer to https://source.android.com/devices/tech/connect/signal-strength on how signal
+ * strength thresholds are used for signal strength reporting.
*
* @return array of integer of the signal thresholds
*
- * @hide
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSSI
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSCP
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSRP
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSRQ
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSSNR
+ * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRP
+ * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRQ
+ * @see #SIGNAL_MEASUREMENT_TYPE_SSSINR
*/
public @NonNull int[] getThresholds() {
return mThresholds.clone();
}
+ /**
+ * Get the minimum number of thresholds allowed in each SignalThresholdInfo.
+ *
+ * @return the minimum number of thresholds allowed
+ */
+ public static int getMinimumNumberOfThresholdsAllowed() {
+ return MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED;
+ }
+
+ /**
+ * Get the maximum number of threshold allowed in each SignalThresholdInfo.
+ *
+ * @return the maximum number of thresholds allowed
+ */
+ public static int getMaximumNumberOfThresholdsAllowed() {
+ return MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED;
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 2190eb1..2f577a9 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -131,10 +131,12 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -15185,4 +15187,17 @@
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 3412079..a5150b0 100644
--- a/telephony/java/android/telephony/euicc/DownloadableSubscription.java
+++ b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
@@ -121,25 +121,21 @@
}
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 @@
*/
@NonNull
public Builder setEncodedActivationCode(@NonNull String value) {
- Preconditions.checkNotNull(encodedActivationCode, "Activation code may not be null");
encodedActivationCode = value;
return this;
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 9a2def9..1a940c7 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 b53b78a..8be3b7e 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.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 @@
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 @@
+ " return half4(sample(bitmapShader, xy).rgb, param1);\n"
+ "}\n";
- private byte[] mUniforms = new byte[4];
-
BitmapsView(Context c) {
super(c);
@@ -86,11 +83,13 @@
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 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 0000000..f6d9a73
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
+import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
+import android.graphics.RuntimeShader;
+import android.os.Bundle;
+import android.os.Trace;
+import android.view.RenderNodeAnimator;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+
+import java.util.ArrayList;
+
+public class RippleActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.addView(new RippleView(this),
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+
+ setContentView(layout);
+ }
+
+ static class RippleView extends View {
+ static final int DURATION = 1000;
+ static final int MAX_RADIUS = 250;
+
+ private boolean mToggle = false;
+ ArrayList<RenderNodeAnimator> mRunningAnimations = new ArrayList<RenderNodeAnimator>();
+
+ CanvasProperty<Float> mX;
+ CanvasProperty<Float> mY;
+ CanvasProperty<Float> mRadius;
+ CanvasProperty<Float> mProgress;
+ CanvasProperty<Paint> mPaint;
+ RuntimeShader mRuntimeShader;
+
+ static final String sSkSL = ""
+ + "uniform float2 in_origin;"
+ + "uniform float in_progress;\n"
+ + "uniform float in_maxRadius;\n"
+ + "uniform shader in_paintColor;\n"
+ + "float dist2(float2 p0, float2 pf) { return sqrt((pf.x - p0.x) * (pf.x - p0.x) + "
+ + "(pf.y - p0.y) * (pf.y - p0.y)); }\n"
+ + "float mod2(float a, float b) { return a - (b * floor(a / b)); }\n"
+ + "float rand(float2 src) { return fract(sin(dot(src.xy, float2(12.9898, 78.233)))"
+ + " * 43758.5453123); }\n"
+ + "float4 main(float2 p)\n"
+ + "{\n"
+ + " float fraction = in_progress;\n"
+ + " float2 fragCoord = p;//sk_FragCoord.xy;\n"
+ + " float maxDist = in_maxRadius;\n"
+ + " float fragDist = dist2(in_origin, fragCoord.xy);\n"
+ + " float circleRadius = maxDist * fraction;\n"
+ + " float colorVal = (fragDist - circleRadius) / maxDist;\n"
+ + " float d = fragDist < circleRadius \n"
+ + " ? 1. - abs(colorVal * 2. * smoothstep(0., 1., fraction)) \n"
+ + " : 1. - abs(colorVal * 3.);\n"
+ + " d = smoothstep(0., 1., d);\n"
+ + " float divider = 2.;\n"
+ + " float x = floor(fragCoord.x / divider);\n"
+ + " float y = floor(fragCoord.y / divider);\n"
+ + " float density = .95;\n"
+ + " d = rand(float2(x, y)) > density ? d : d * .2;\n"
+ + " d = d * rand(float2(fraction, x * y));\n"
+ + " float alpha = 1. - pow(fraction, 3.);\n"
+ + " return float4(sample(in_paintColor).rgb, d * alpha);\n"
+ + "}";
+
+ RippleView(Context c) {
+ super(c);
+ setClickable(true);
+
+ mX = CanvasProperty.createFloat(200.0f);
+ mY = CanvasProperty.createFloat(200.0f);
+ mRadius = CanvasProperty.createFloat(150.0f);
+ mProgress = CanvasProperty.createFloat(0.0f);
+
+ Paint p = new Paint();
+ p.setAntiAlias(true);
+ p.setColor(0xFFFF0000);
+ mPaint = CanvasProperty.createPaint(p);
+
+ mRuntimeShader = new RuntimeShader(sSkSL, false);
+ mRuntimeShader.setUniform("in_maxRadius", MAX_RADIUS);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ if (canvas.isHardwareAccelerated()) {
+ RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
+ recordingCanvas.drawRipple(mX, mY, mRadius, mPaint, mProgress, mRuntimeShader);
+ }
+ }
+
+ @Override
+ public boolean performClick() {
+ for (int i = 0; i < mRunningAnimations.size(); i++) {
+ mRunningAnimations.get(i).cancel();
+ }
+ mRunningAnimations.clear();
+
+ mToggle = !mToggle;
+
+ mRunningAnimations.add(new RenderNodeAnimator(
+ mX, mToggle ? 400.0f : 200.0f));
+
+ mRunningAnimations.add(new RenderNodeAnimator(
+ mY, mToggle ? 600.0f : 200.0f));
+
+ mRunningAnimations.add(new RenderNodeAnimator(
+ mRadius, mToggle ? MAX_RADIUS : 150.0f));
+
+ mRunningAnimations.add(new RenderNodeAnimator(
+ mProgress, mToggle ? 1.0f : 0.0f));
+
+ mRunningAnimations.add(new RenderNodeAnimator(
+ mPaint, RenderNodeAnimator.PAINT_ALPHA, 64.0f));
+
+ // Will be "chained" to run after the above
+ mRunningAnimations.add(new RenderNodeAnimator(
+ mPaint, RenderNodeAnimator.PAINT_ALPHA, 255.0f));
+
+ for (int i = 0; i < mRunningAnimations.size(); i++) {
+ RenderNodeAnimator anim = mRunningAnimations.get(i);
+ anim.setDuration(DURATION);
+ anim.setTarget(this);
+ if (i == (mRunningAnimations.size() - 1)) {
+ // "chain" test
+ anim.setStartValue(64.0f);
+ anim.setStartDelay(anim.getDuration());
+ }
+ anim.start();
+ }
+
+ if (mToggle) {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "pretendBusy");
+ try {
+ Thread.sleep(DURATION);
+ } catch (InterruptedException e) {
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ });
+ }
+ return true;
+ }
+ }
+}
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index 2a601e5..ad8aac1 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 @@
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 9e1ea2e..2201efd 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 @@
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 1a236c6..d6355f5 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 beb4049..95fd959 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 @@
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 2cb16d3372..b2bcfeb 100644
--- a/tests/net/common/java/android/net/CaptivePortalDataTest.kt
+++ b/tests/net/common/java/android/net/CaptivePortalDataTest.kt
@@ -58,7 +58,8 @@
@Test
fun testParcelUnparcel() {
- assertParcelSane(data, fieldCount = 8)
+ val fieldCount = if (SdkLevel.isAtLeastS()) 8 else 7
+ assertParcelSane(data, fieldCount)
assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build())
assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build())
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 8e18751..16c4865 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.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 @@
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 @@
@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 @@
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 a836e81..4630269 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.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.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 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.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -367,8 +369,6 @@
private HandlerThread mAlarmManagerThread;
private TestNetIdManager mNetIdManager;
- @Mock IpConnectivityMetrics.Logger mMetricsService;
- @Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
@Mock DeviceIdleInternal mDeviceIdleInternal;
@Mock INetworkManagementService mNetworkManagementService;
@Mock INetworkStatsService mStatsService;
@@ -388,6 +388,7 @@
@Mock MockableSystemProperties mSystemProperties;
@Mock EthernetManager mEthernetManager;
@Mock NetworkPolicyManager mNetworkPolicyManager;
+ @Mock KeyStore mKeyStore;
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -1066,6 +1067,15 @@
private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
private VpnInfo mVpnInfo;
+ // These ConditionVariables allow tests to wait for LegacyVpnRunner to be stopped/started.
+ // TODO: this scheme is ad-hoc and error-prone because it does not fail if, for example, the
+ // test expects two starts in a row, or even if the production code calls start twice in a
+ // row. find a better solution. Simply putting a method to create a LegacyVpnRunner into
+ // Vpn.Dependencies doesn't work because LegacyVpnRunner is not a static class and has
+ // extensive access into the internals of Vpn.
+ private ConditionVariable mStartLegacyVpnCv = new ConditionVariable();
+ private ConditionVariable mStopVpnRunnerCv = new ConditionVariable();
+
public MockVpn(int userId) {
super(startHandlerThreadAndReturnLooper(), mServiceContext,
new Dependencies() {
@@ -1079,7 +1089,7 @@
return mDeviceIdleInternal;
}
},
- mNetworkManagementService, mMockNetd, userId, mock(KeyStore.class));
+ mNetworkManagementService, mMockNetd, userId, mKeyStore);
}
public void setUids(Set<UidRange> uids) {
@@ -1191,10 +1201,44 @@
}
mAgentRegistered = false;
setUids(null);
+ // Remove NET_CAPABILITY_INTERNET or MockNetworkAgent will refuse to connect later on.
+ mNetworkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
mInterface = null;
}
@Override
+ public void startLegacyVpnRunner() {
+ mStartLegacyVpnCv.open();
+ }
+
+ public void expectStartLegacyVpnRunner() {
+ assertTrue("startLegacyVpnRunner not called after " + TIMEOUT_MS + " ms",
+ mStartLegacyVpnCv.block(TIMEOUT_MS));
+
+ // startLegacyVpn calls stopVpnRunnerPrivileged, which will open mStopVpnRunnerCv, just
+ // before calling startLegacyVpnRunner. Restore mStopVpnRunnerCv, so the test can expect
+ // that the VpnRunner is stopped and immediately restarted by calling
+ // expectStartLegacyVpnRunner() and expectStopVpnRunnerPrivileged() back-to-back.
+ mStopVpnRunnerCv = new ConditionVariable();
+ }
+
+ @Override
+ public void stopVpnRunnerPrivileged() {
+ if (mVpnRunner != null) {
+ super.stopVpnRunnerPrivileged();
+ disconnect();
+ mStartLegacyVpnCv = new ConditionVariable();
+ }
+ mVpnRunner = null;
+ mStopVpnRunnerCv.open();
+ }
+
+ public void expectStopVpnRunnerPrivileged() {
+ assertTrue("stopVpnRunnerPrivileged not called after " + TIMEOUT_MS + " ms",
+ mStopVpnRunnerCv.block(TIMEOUT_MS));
+ }
+
+ @Override
public synchronized VpnInfo getVpnInfo() {
if (mVpnInfo != null) return mVpnInfo;
@@ -1275,10 +1319,19 @@
}
}
- private static final int VPN_USER = 0;
- private static final int APP1_UID = UserHandle.getUid(VPN_USER, 10100);
- private static final int APP2_UID = UserHandle.getUid(VPN_USER, 10101);
- private static final int VPN_UID = UserHandle.getUid(VPN_USER, 10043);
+ private static final int PRIMARY_USER = 0;
+ private static final int APP1_UID = UserHandle.getUid(PRIMARY_USER, 10100);
+ private static final int APP2_UID = UserHandle.getUid(PRIMARY_USER, 10101);
+ private static final int VPN_UID = UserHandle.getUid(PRIMARY_USER, 10043);
+ private static final UserInfo PRIMARY_USER_INFO = new UserInfo(PRIMARY_USER, "",
+ UserInfo.FLAG_PRIMARY);
+
+ private static final int RESTRICTED_USER = 1;
+ private static final UserInfo RESTRICTED_USER_INFO = new UserInfo(RESTRICTED_USER, "",
+ UserInfo.FLAG_RESTRICTED);
+ static {
+ RESTRICTED_USER_INFO.restrictedProfileParentId = PRIMARY_USER;
+ }
@Before
public void setUp() throws Exception {
@@ -1287,12 +1340,14 @@
mContext = InstrumentationRegistry.getContext();
MockitoAnnotations.initMocks(this);
- when(mMetricsService.defaultNetworkMetrics()).thenReturn(mDefaultNetworkMetrics);
- when(mUserManager.getAliveUsers()).thenReturn(
- Arrays.asList(new UserInfo[] {
- new UserInfo(VPN_USER, "", 0),
- }));
+ when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO));
+ when(mUserManager.getUserInfo(PRIMARY_USER)).thenReturn(PRIMARY_USER_INFO);
+ // canHaveRestrictedProfile does not take a userId. It applies to the userId of the context
+ // it was started from, i.e., PRIMARY_USER.
+ when(mUserManager.canHaveRestrictedProfile()).thenReturn(true);
+ when(mUserManager.getUserInfo(RESTRICTED_USER)).thenReturn(RESTRICTED_USER_INFO);
+
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
@@ -1362,9 +1417,9 @@
doReturn(mNetworkStack).when(deps).getNetworkStack();
doReturn(mSystemProperties).when(deps).getSystemProperties();
doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
- doReturn(mMetricsService).when(deps).getMetricsLogger();
doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
doReturn(mBatteryStatsService).when(deps).getBatteryStatsService();
+ doReturn(mKeyStore).when(deps).getKeyStore();
doAnswer(inv -> {
mPolicyTracker = new WrappedMultinetworkPolicyTracker(
inv.getArgument(0), inv.getArgument(1), inv.getArgument(2));
@@ -1533,7 +1588,7 @@
waitForIdle();
try {
final Intent intent = get(timeoutMs, TimeUnit.MILLISECONDS);
- fail("Unexpected broadcast: " + intent.getAction());
+ fail("Unexpected broadcast: " + intent.getAction() + " " + intent.getExtras());
} catch (TimeoutException expected) {
} finally {
mServiceContext.unregisterReceiver(mReceiver);
@@ -2074,10 +2129,6 @@
@Test
public void testOwnerUidCannotChange() throws Exception {
- // Owner UIDs are not visible without location permission.
- setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION);
-
final NetworkCapabilities ncTemplate = new NetworkCapabilities();
final int originalOwnerUid = Process.myUid();
ncTemplate.setOwnerUid(originalOwnerUid);
@@ -2097,6 +2148,10 @@
mWiFiNetworkAgent.setNetworkCapabilities(agentCapabilities, true);
waitForIdle();
+ // Owner UIDs are not visible without location permission.
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+
// Check that the capability change has been applied but the owner UID is not modified.
NetworkCapabilities nc = mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork());
assertEquals(originalOwnerUid, nc.getOwnerUid());
@@ -5837,6 +5892,131 @@
mCm.unregisterNetworkCallback(callback);
}
+ private void assertGetNetworkInfoOfGetActiveNetworkIsConnected(boolean expectedConnectivity) {
+ // What Chromium used to do before https://chromium-review.googlesource.com/2605304
+ assertEquals("Unexpected result for getActiveNetworkInfo(getActiveNetwork())",
+ expectedConnectivity, mCm.getNetworkInfo(mCm.getActiveNetwork()).isConnected());
+ }
+
+ @Test
+ public void testVpnUnderlyingNetworkSuspended() throws Exception {
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(callback);
+
+ // Connect a VPN.
+ mMockVpn.establishForMyUid(false /* validated */, true /* hasInternet */,
+ false /* isStrictMode */);
+ callback.expectAvailableCallbacksUnvalidated(mMockVpn);
+
+ // Connect cellular data.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(false /* validated */);
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_CELLULAR));
+ callback.assertNoCallback();
+
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(true);
+
+ // Suspend the cellular network and expect the VPN to be suspended.
+ mCellNetworkAgent.suspend();
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_CELLULAR));
+ callback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
+ callback.assertNoCallback();
+
+ assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED);
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
+ // VPN's main underlying network is suspended, so no connectivity.
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+
+ // Switch to another network. The VPN should no longer be suspended.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false /* validated */);
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_WIFI));
+
+ // BUG: the VPN is no longer suspended, so a RESUMED callback should have been sent.
+ // callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
+ callback.assertNoCallback();
+
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED.
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ // BUG: the device has connectivity, so this should return true.
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+
+ // Unsuspend cellular and then switch back to it.
+ // The same bug happens in the opposite direction: the VPN's capabilities correctly have
+ // NOT_SUSPENDED, but the VPN's NetworkInfo is in state SUSPENDED.
+ mCellNetworkAgent.resume();
+ callback.assertNoCallback();
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_CELLULAR));
+ // Spurious double callback?
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_CELLULAR));
+ callback.assertNoCallback();
+
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED.
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ // BUG: the device has connectivity, so this should return true.
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+
+ // Re-suspending the current network fixes the problem.
+ mCellNetworkAgent.suspend();
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_CELLULAR));
+ callback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
+ callback.assertNoCallback();
+
+ assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED);
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+
+ mCellNetworkAgent.resume();
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_CELLULAR));
+ callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
+ callback.assertNoCallback();
+
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(true);
+ }
+
@Test
public void testVpnNetworkActive() throws Exception {
final int uid = Process.myUid();
@@ -6315,7 +6495,7 @@
}
@Test
- public void testVpnRestrictedUsers() throws Exception {
+ public void testRestrictedProfileAffectsVpnUidRanges() throws Exception {
// NETWORK_SETTINGS is necessary to see the UID ranges in NetworkCapabilities.
mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS,
PERMISSION_GRANTED);
@@ -6347,19 +6527,11 @@
callback.expectCapabilitiesThat(mWiFiNetworkAgent, (caps)
-> caps.hasCapability(NET_CAPABILITY_VALIDATED));
- // Create a fake restricted profile whose parent is our user ID.
- final int userId = UserHandle.getUserId(uid);
- when(mUserManager.canHaveRestrictedProfile()).thenReturn(true);
- final int restrictedUserId = userId + 1;
- final UserInfo info = new UserInfo(restrictedUserId, "user", UserInfo.FLAG_RESTRICTED);
- info.restrictedProfileParentId = userId;
- assertTrue(info.isRestricted());
- when(mUserManager.getUserInfo(restrictedUserId)).thenReturn(info);
- when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, restrictedUserId))
- .thenReturn(UserHandle.getUid(restrictedUserId, VPN_UID));
+ when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER))
+ .thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID));
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
- addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
// Send a USER_ADDED broadcast for it.
// The BroadcastReceiver for this broadcast checks that is being run on the handler thread.
@@ -6371,7 +6543,7 @@
callback.expectCapabilitiesThat(mMockVpn, (caps)
-> caps.getUids().size() == 2
&& caps.getUids().contains(new UidRange(uid, uid))
- && caps.getUids().contains(UidRange.createForUser(restrictedUserId))
+ && caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER))
&& caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_WIFI));
@@ -6381,13 +6553,13 @@
callback.expectCapabilitiesThat(mMockVpn, (caps)
-> caps.getUids().size() == 2
&& caps.getUids().contains(new UidRange(uid, uid))
- && caps.getUids().contains(UidRange.createForUser(restrictedUserId))
+ && caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER))
&& caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_WIFI));
// Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
- removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId);
+ removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
// Expect that the VPN gains the UID range for the restricted user, and that the capability
@@ -6397,53 +6569,72 @@
&& caps.getUids().contains(new UidRange(uid, uid))
&& caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_WIFI));
+ }
- // Test lockdown with restricted profiles.
+ @Test
+ public void testLockdownVpnWithRestrictedProfiles() throws Exception {
+ // For ConnectivityService#setAlwaysOnVpnPackage.
mServiceContext.setPermission(
Manifest.permission.CONTROL_ALWAYS_ON_VPN, PERMISSION_GRANTED);
+ // For call Vpn#setAlwaysOnPackage.
mServiceContext.setPermission(
Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED);
+ // Necessary to see the UID ranges in NetworkCapabilities.
mServiceContext.setPermission(
Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .build();
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, callback);
+
+ final int uid = Process.myUid();
+
// Connect wifi and check that UIDs in the main and restricted profiles have network access.
- mMockVpn.disconnect();
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true /* validated */);
- final int restrictedUid = UserHandle.getUid(restrictedUserId, 42 /* appId */);
+ final int restrictedUid = UserHandle.getUid(RESTRICTED_USER, 42 /* appId */);
assertNotNull(mCm.getActiveNetworkForUid(uid));
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
// Enable always-on VPN lockdown. The main user loses network access because no VPN is up.
final ArrayList<String> allowList = new ArrayList<>();
- mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+ mService.setAlwaysOnVpnPackage(PRIMARY_USER, ALWAYS_ON_PACKAGE, true /* lockdown */,
+ allowList);
waitForIdle();
assertNull(mCm.getActiveNetworkForUid(uid));
+ // This is arguably overspecified: a UID that is not running doesn't have an active network.
+ // But it's useful to check that non-default users do not lose network access, and to prove
+ // that the loss of connectivity below is indeed due to the restricted profile coming up.
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
// Start the restricted profile, and check that the UID within it loses network access.
- when(mUserManager.getAliveUsers()).thenReturn(
- Arrays.asList(new UserInfo[] {
- new UserInfo(userId, "", 0),
- info
- }));
+ when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER))
+ .thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID));
+ when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO,
+ RESTRICTED_USER_INFO));
// TODO: check that VPN app within restricted profile still has access, etc.
+ final Intent addedIntent = new Intent(ACTION_USER_ADDED);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
+ final Handler handler = new Handler(mCsHandlerThread.getLooper());
handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
waitForIdle();
assertNull(mCm.getActiveNetworkForUid(uid));
assertNull(mCm.getActiveNetworkForUid(restrictedUid));
// Stop the restricted profile, and check that the UID within it has network access again.
- when(mUserManager.getAliveUsers()).thenReturn(
- Arrays.asList(new UserInfo[] {
- new UserInfo(userId, "", 0),
- }));
+ when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO));
+
+ // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
+ final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
+ removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
waitForIdle();
assertNull(mCm.getActiveNetworkForUid(uid));
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
- mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+ mService.setAlwaysOnVpnPackage(PRIMARY_USER, null, false /* lockdown */, allowList);
waitForIdle();
}
@@ -6784,6 +6975,7 @@
final int userId = UserHandle.getUserId(uid);
final ArrayList<String> allowList = new ArrayList<>();
mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+ waitForIdle();
UidRangeParcel firstHalf = new UidRangeParcel(1, VPN_UID - 1);
UidRangeParcel secondHalf = new UidRangeParcel(VPN_UID + 1, 99999);
@@ -6805,10 +6997,10 @@
// Disable lockdown, expect to see the network unblocked.
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
- expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf);
callback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
vpnUidCallback.assertNoCallback();
+ expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
@@ -6851,9 +7043,11 @@
// Disable lockdown, remove our UID from the allowlist, and re-enable lockdown.
// Everything should now be blocked.
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+ waitForIdle();
expectNetworkRejectNonSecureVpn(inOrder, false, piece1, piece2, piece3);
allowList.clear();
mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+ waitForIdle();
expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf);
defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent);
@@ -6931,6 +7125,200 @@
mCm.unregisterNetworkCallback(vpnUidCallback);
}
+ private void setupLegacyLockdownVpn() {
+ final String profileName = "testVpnProfile";
+ final byte[] profileTag = profileName.getBytes(StandardCharsets.UTF_8);
+ when(mKeyStore.contains(Credentials.LOCKDOWN_VPN)).thenReturn(true);
+ when(mKeyStore.get(Credentials.LOCKDOWN_VPN)).thenReturn(profileTag);
+
+ final VpnProfile profile = new VpnProfile(profileName);
+ profile.name = "My VPN";
+ profile.server = "192.0.2.1";
+ profile.dnsServers = "8.8.8.8";
+ profile.type = VpnProfile.TYPE_IPSEC_XAUTH_PSK;
+ final byte[] encodedProfile = profile.encode();
+ when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile);
+ }
+
+ @Test
+ public void testLegacyLockdownVpn() throws Exception {
+ mServiceContext.setPermission(
+ Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED);
+
+ final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, callback);
+
+ final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(defaultCallback);
+
+ // Pretend lockdown VPN was configured.
+ setupLegacyLockdownVpn();
+
+ // LockdownVpnTracker disables the Vpn teardown code and enables lockdown.
+ // Check the VPN's state before it does so.
+ assertTrue(mMockVpn.getEnableTeardown());
+ assertFalse(mMockVpn.getLockdown());
+
+ // Send a USER_UNLOCKED broadcast so CS starts LockdownVpnTracker.
+ final int userId = UserHandle.getUserId(Process.myUid());
+ final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ final Handler handler = new Handler(mCsHandlerThread.getLooper());
+ handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
+ waitForIdle();
+
+ // Lockdown VPN disables teardown and enables lockdown.
+ assertFalse(mMockVpn.getEnableTeardown());
+ assertTrue(mMockVpn.getLockdown());
+
+ // Bring up a network.
+ // Expect nothing to happen because the network does not have an IPv4 default route: legacy
+ // VPN only supports IPv4.
+ final LinkProperties cellLp = new LinkProperties();
+ cellLp.setInterfaceName("rmnet0");
+ cellLp.addLinkAddress(new LinkAddress("2001:db8::1/64"));
+ cellLp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, "rmnet0"));
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
+ mCellNetworkAgent.connect(false /* validated */);
+ callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
+ waitForIdle();
+ assertNull(mMockVpn.getAgent());
+
+ // Add an IPv4 address. Ideally the VPN should start, but it doesn't because nothing calls
+ // LockdownVpnTracker#handleStateChangedLocked. This is a bug.
+ // TODO: consider fixing this.
+ cellLp.addLinkAddress(new LinkAddress("192.0.2.2/25"));
+ cellLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "rmnet0"));
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ callback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ waitForIdle();
+ assertNull(mMockVpn.getAgent());
+
+ // Disconnect, then try again with a network that supports IPv4 at connection time.
+ // Expect lockdown VPN to come up.
+ ExpectedBroadcast b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
+ mCellNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ b1.expectBroadcast();
+
+ // When lockdown VPN is active, the NetworkInfo state in CONNECTIVITY_ACTION is overwritten
+ // with the state of the VPN network. So expect a CONNECTING broadcast.
+ b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTING);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
+ mCellNetworkAgent.connect(false /* validated */);
+ callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
+ b1.expectBroadcast();
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED);
+
+ // TODO: it would be nice if we could simply rely on the production code here, and have
+ // LockdownVpnTracker start the VPN, have the VPN code register its NetworkAgent with
+ // ConnectivityService, etc. That would require duplicating a fair bit of code from the
+ // Vpn tests around how to mock out LegacyVpnRunner. But even if we did that, this does not
+ // work for at least two reasons:
+ // 1. In this test, calling registerNetworkAgent does not actually result in an agent being
+ // registered. This is because nothing calls onNetworkMonitorCreated, which is what
+ // actually ends up causing handleRegisterNetworkAgent to be called. Code in this test
+ // that wants to register an agent must use TestNetworkAgentWrapper.
+ // 2. Even if we exposed Vpn#agentConnect to the test, and made MockVpn#agentConnect call
+ // the TestNetworkAgentWrapper code, this would deadlock because the
+ // TestNetworkAgentWrapper code cannot be called on the handler thread since it calls
+ // waitForIdle().
+ mMockVpn.expectStartLegacyVpnRunner();
+ b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
+ ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
+ mMockVpn.establishForMyUid();
+ callback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ b1.expectBroadcast();
+ b2.expectBroadcast();
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+
+ // Switch default network from cell to wifi. Expect VPN to disconnect and reconnect.
+ final LinkProperties wifiLp = new LinkProperties();
+ wifiLp.setInterfaceName("wlan0");
+ wifiLp.addLinkAddress(new LinkAddress("192.0.2.163/25"));
+ wifiLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "wlan0"));
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
+
+ b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
+ // Wifi is CONNECTING because the VPN isn't up yet.
+ b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTING);
+ ExpectedBroadcast b3 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);
+ mWiFiNetworkAgent.connect(false /* validated */);
+ b1.expectBroadcast();
+ b2.expectBroadcast();
+ b3.expectBroadcast();
+ mMockVpn.expectStopVpnRunnerPrivileged();
+ mMockVpn.expectStartLegacyVpnRunner();
+
+ // TODO: why is wifi not blocked? Is it because when this callback is sent, the VPN is still
+ // connected, so the network is not considered blocked by the lockdown UID ranges? But the
+ // fact that a VPN is connected should only result in the VPN itself being unblocked, not
+ // any other network. Bug in isUidBlockedByVpn?
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ callback.expectCapabilitiesThat(mMockVpn, nc -> nc.hasTransport(TRANSPORT_WIFI));
+ callback.expectCallback(CallbackEntry.LOST, mMockVpn);
+ defaultCallback.expectCapabilitiesThat(mMockVpn, nc -> nc.hasTransport(TRANSPORT_WIFI));
+ defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+ defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
+
+ // While the VPN is reconnecting on the new network, everything is blocked.
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED);
+
+ // The VPN comes up again on wifi.
+ b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
+ b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
+ mMockVpn.establishForMyUid();
+ callback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ b1.expectBroadcast();
+ b2.expectBroadcast();
+
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+
+ // Disconnect cell. Nothing much happens since it's not the default network.
+ // Whenever LockdownVpnTracker is connected, it will send a connected broadcast any time any
+ // NetworkInfo is updated. This is probably a bug.
+ // TODO: consider fixing this.
+ b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
+ mCellNetworkAgent.disconnect();
+ b1.expectBroadcast();
+ callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ defaultCallback.assertNoCallback();
+
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+
+ b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ b1.expectBroadcast();
+ callback.expectCapabilitiesThat(mMockVpn, nc -> !nc.hasTransport(TRANSPORT_WIFI));
+ b2 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);
+ mMockVpn.expectStopVpnRunnerPrivileged();
+ callback.expectCallback(CallbackEntry.LOST, mMockVpn);
+ b2.expectBroadcast();
+ }
+
@Test
public final void testLoseTrusted() throws Exception {
final NetworkRequest trustedRequest = new NetworkRequest.Builder()
@@ -7605,7 +7993,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
// The uid range needs to cover the test app so the network is visible to it.
- final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
+ final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
mMockVpn.establish(lp, VPN_UID, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
@@ -7633,7 +8021,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
- final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
+ final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
@@ -7649,7 +8037,7 @@
lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun0"));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
// The uid range needs to cover the test app so the network is visible to it.
- final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
+ final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
@@ -7664,7 +8052,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
- final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
+ final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
mMockVpn.establish(lp, VPN_UID, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
@@ -7716,7 +8104,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
- final UidRange vpnRange = UidRange.createForUser(VPN_USER);
+ final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER);
final Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
mMockVpn.establish(lp, VPN_UID, vpnRanges);
assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
@@ -7781,8 +8169,22 @@
naExtraInfo.unregister();
}
+ // To avoid granting location permission bypass.
+ private void denyAllLocationPrivilegedPermissions() {
+ mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ PERMISSION_DENIED);
+ mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS,
+ PERMISSION_DENIED);
+ mServiceContext.setPermission(Manifest.permission.NETWORK_STACK,
+ PERMISSION_DENIED);
+ mServiceContext.setPermission(Manifest.permission.NETWORK_SETUP_WIZARD,
+ PERMISSION_DENIED);
+ }
+
private void setupLocationPermissions(
int targetSdk, boolean locationToggle, String op, String perm) throws Exception {
+ denyAllLocationPrivilegedPermissions();
+
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.targetSdkVersion = targetSdk;
when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
@@ -7900,7 +8302,7 @@
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
throws Exception {
- final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
+ final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
mMockVpn.setVpnType(vpnType);
@@ -8104,11 +8506,18 @@
assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
}
+ public NetworkAgentInfo fakeMobileNai(NetworkCapabilities nc) {
+ final NetworkInfo info = new NetworkInfo(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_LTE,
+ ConnectivityManager.getNetworkTypeName(TYPE_MOBILE),
+ TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE));
+ return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(),
+ nc, 0, mServiceContext, null, new NetworkAgentConfig(), mService, null, null, null,
+ 0, INVALID_UID);
+ }
+
@Test
public void testCheckConnectivityDiagnosticsPermissionsNetworkStack() throws Exception {
- final NetworkAgentInfo naiWithoutUid =
- new NetworkAgentInfo(null, null, null, null, new NetworkCapabilities(), 0,
- mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
+ final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities());
mServiceContext.setPermission(
android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
@@ -8121,9 +8530,7 @@
@Test
public void testCheckConnectivityDiagnosticsPermissionsWrongUidPackageName() throws Exception {
- final NetworkAgentInfo naiWithoutUid =
- new NetworkAgentInfo(null, null, null, null, new NetworkCapabilities(), 0,
- mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
+ final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities());
mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
@@ -8136,9 +8543,7 @@
@Test
public void testCheckConnectivityDiagnosticsPermissionsNoLocationPermission() throws Exception {
- final NetworkAgentInfo naiWithoutUid =
- new NetworkAgentInfo(null, null, null, null, new NetworkCapabilities(), 0,
- mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
+ final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities());
mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
@@ -8151,22 +8556,17 @@
@Test
public void testCheckConnectivityDiagnosticsPermissionsActiveVpn() throws Exception {
- final Network network = new Network(NET_ID);
- final NetworkAgentInfo naiWithoutUid =
- new NetworkAgentInfo(null, network, null, null, new NetworkCapabilities(), 0,
- mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
-
- setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION);
+ final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities());
mMockVpn.establishForMyUid();
assertUidRangesUpdatedForMyUid(true);
// Wait for networks to connect and broadcasts to be sent before removing permissions.
waitForIdle();
- mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
- assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {network}));
+ assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {naiWithoutUid.network}));
waitForIdle();
assertTrue(
"Active VPN permission not applied",
@@ -8187,9 +8587,7 @@
public void testCheckConnectivityDiagnosticsPermissionsNetworkAdministrator() throws Exception {
final NetworkCapabilities nc = new NetworkCapabilities();
nc.setAdministratorUids(new int[] {Process.myUid()});
- final NetworkAgentInfo naiWithUid =
- new NetworkAgentInfo(null, null, null, null, nc, 0, mServiceContext, null, null,
- mService, null, null, null, 0, INVALID_UID);
+ final NetworkAgentInfo naiWithUid = fakeMobileNai(nc);
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
@@ -8206,9 +8604,7 @@
final NetworkCapabilities nc = new NetworkCapabilities();
nc.setOwnerUid(Process.myUid());
nc.setAdministratorUids(new int[] {Process.myUid()});
- final NetworkAgentInfo naiWithUid =
- new NetworkAgentInfo(null, null, null, null, nc, 0, mServiceContext, null, null,
- mService, null, null, null, 0, INVALID_UID);
+ final NetworkAgentInfo naiWithUid = fakeMobileNai(nc);
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
@@ -8475,7 +8871,7 @@
lp.setInterfaceName("tun0");
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
- final UidRange vpnRange = UidRange.createForUser(VPN_USER);
+ final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER);
Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
mMockVpn.establish(lp, VPN_UID, vpnRanges);
assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
@@ -8491,4 +8887,20 @@
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 96c56e3..4d151af 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.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 @@
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 3c08d34..c04ddd7 100644
--- a/tests/vcn/Android.bp
+++ b/tests/vcn/Android.bp
@@ -16,6 +16,7 @@
"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 0000000..9c6b719
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.net.vcn.VcnManager.VcnUnderlyingNetworkPolicyListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.util.concurrent.Executor;
+
+public class VcnManagerTest {
+ private static final Executor INLINE_EXECUTOR = Runnable::run;
+
+ private IVcnManagementService mMockVcnManagementService;
+ private VcnUnderlyingNetworkPolicyListener mMockPolicyListener;
+
+ private Context mContext;
+ private VcnManager mVcnManager;
+
+ @Before
+ public void setUp() {
+ mMockVcnManagementService = mock(IVcnManagementService.class);
+ mMockPolicyListener = mock(VcnUnderlyingNetworkPolicyListener.class);
+
+ mContext = getContext();
+ mVcnManager = new VcnManager(mContext, mMockVcnManagementService);
+ }
+
+ @Test
+ public void testAddVcnUnderlyingNetworkPolicyListener() throws Exception {
+ mVcnManager.addVcnUnderlyingNetworkPolicyListener(INLINE_EXECUTOR, mMockPolicyListener);
+
+ ArgumentCaptor<IVcnUnderlyingNetworkPolicyListener> captor =
+ ArgumentCaptor.forClass(IVcnUnderlyingNetworkPolicyListener.class);
+ verify(mMockVcnManagementService).addVcnUnderlyingNetworkPolicyListener(captor.capture());
+
+ assertTrue(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener));
+
+ IVcnUnderlyingNetworkPolicyListener listenerWrapper = captor.getValue();
+ listenerWrapper.onPolicyChanged();
+ verify(mMockPolicyListener).onPolicyChanged();
+ }
+
+ @Test
+ public void testRemoveVcnUnderlyingNetworkPolicyListener() throws Exception {
+ mVcnManager.addVcnUnderlyingNetworkPolicyListener(INLINE_EXECUTOR, mMockPolicyListener);
+
+ mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener));
+ verify(mMockVcnManagementService)
+ .addVcnUnderlyingNetworkPolicyListener(
+ any(IVcnUnderlyingNetworkPolicyListener.class));
+ }
+
+ @Test
+ public void testRemoveVcnUnderlyingNetworkPolicyListenerUnknownListener() throws Exception {
+ mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener));
+ verify(mMockVcnManagementService, never())
+ .addVcnUnderlyingNetworkPolicyListener(
+ any(IVcnUnderlyingNetworkPolicyListener.class));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testAddVcnUnderlyingNetworkPolicyListenerNullExecutor() throws Exception {
+ mVcnManager.addVcnUnderlyingNetworkPolicyListener(null, mMockPolicyListener);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testAddVcnUnderlyingNetworkPolicyListenerNullListener() throws Exception {
+ mVcnManager.addVcnUnderlyingNetworkPolicyListener(INLINE_EXECUTOR, null);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testRemoveVcnUnderlyingNetworkPolicyListenerNullListener() {
+ mVcnManager.removeVcnUnderlyingNetworkPolicyListener(null);
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java
new file mode 100644
index 0000000..3ba0a1f
--- /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);
+ }
+}