Merge "Add APIs that can notify client ui translation state."
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 ae32fba..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();
@@ -162,7 +169,7 @@
public static final class PutDocumentsRequest.Builder {
ctor public PutDocumentsRequest.Builder();
method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocument(@NonNull android.app.appsearch.GenericDocument...);
- method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocument(@NonNull java.util.Collection<android.app.appsearch.GenericDocument>);
+ method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocument(@NonNull java.util.Collection<? extends android.app.appsearch.GenericDocument>);
method @NonNull public android.app.appsearch.PutDocumentsRequest build();
}
@@ -182,6 +189,7 @@
public final class SearchResult {
method @NonNull public android.app.appsearch.GenericDocument getDocument();
method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches();
+ method @NonNull public String getPackageName();
}
public static final class SearchResult.MatchInfo {
@@ -204,9 +212,11 @@
}
public final class SearchSpec {
+ method @NonNull public java.util.List<java.lang.String> getFilterPackageNames();
method public int getMaxSnippetSize();
method @NonNull public java.util.List<java.lang.String> getNamespaces();
method public int getOrder();
+ method @NonNull public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getProjections();
method public int getRankingStrategy();
method public int getResultCountPerPage();
method @NonNull public java.util.List<java.lang.String> getSchemaTypes();
@@ -215,17 +225,23 @@
method public int getTermMatch();
field public static final int ORDER_ASCENDING = 1; // 0x1
field public static final int ORDER_DESCENDING = 0; // 0x0
+ field public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
field public static final int RANKING_STRATEGY_CREATION_TIMESTAMP = 2; // 0x2
field public static final int RANKING_STRATEGY_DOCUMENT_SCORE = 1; // 0x1
field public static final int RANKING_STRATEGY_NONE = 0; // 0x0
+ field public static final int RANKING_STRATEGY_RELEVANCE_SCORE = 3; // 0x3
field public static final int TERM_MATCH_EXACT_ONLY = 1; // 0x1
field public static final int TERM_MATCH_PREFIX = 2; // 0x2
}
public static final class SearchSpec.Builder {
ctor public SearchSpec.Builder();
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterPackageNames(@NonNull java.lang.String...);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterPackageNames(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.SearchSpec.Builder addNamespace(@NonNull java.lang.String...);
method @NonNull public android.app.appsearch.SearchSpec.Builder addNamespace(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addProjection(@NonNull String, @NonNull java.lang.String...);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.SearchSpec.Builder addSchemaType(@NonNull java.lang.String...);
method @NonNull public android.app.appsearch.SearchSpec.Builder addSchemaType(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.SearchSpec build();
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 685e5ff..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;
@@ -28,6 +27,7 @@
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -122,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}
@@ -146,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) {
@@ -234,6 +232,7 @@
DEFAULT_DATABASE_NAME,
schemaBundles,
new ArrayList<>(request.getSchemasNotVisibleToSystemUi()),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
request.isForceOverride(),
mContext.getUserId(),
new IAppSearchResultCallback.Stub() {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index d628fb5..0427577 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -22,11 +22,13 @@
import android.os.Bundle;
import android.os.ParcelableException;
import android.os.RemoteException;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import com.android.internal.util.Preconditions;
+import java.io.Closeable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -41,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
@@ -161,11 +163,22 @@
for (AppSearchSchema schema : request.getSchemas()) {
schemaBundles.add(schema.getBundle());
}
+ Map<String, List<Bundle>> schemasPackageAccessibleBundles =
+ new ArrayMap<>(request.getSchemasVisibleToPackagesInternal().size());
+ for (Map.Entry<String, Set<PackageIdentifier>> entry :
+ request.getSchemasVisibleToPackagesInternal().entrySet()) {
+ List<Bundle> packageIdentifierBundles = new ArrayList<>(entry.getValue().size());
+ for (PackageIdentifier packageIdentifier : entry.getValue()) {
+ packageIdentifierBundles.add(packageIdentifier.getBundle());
+ }
+ schemasPackageAccessibleBundles.put(entry.getKey(), packageIdentifierBundles);
+ }
try {
mService.setSchema(
mDatabaseName,
schemaBundles,
new ArrayList<>(request.getSchemasNotVisibleToSystemUi()),
+ schemasPackageAccessibleBundles,
request.isForceOverride(),
mUserId,
new IAppSearchResultCallback.Stub() {
@@ -478,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/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index af8b613..2b43777 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -34,6 +34,8 @@
* @param schemaBundles List of {@link AppSearchSchema} bundles.
* @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform
* surfaces.
+ * @param schemasPackageAccessibleBundles Schema types that are visible to the specified
+ * packages. The value List contains PackageIdentifier Bundles.
* @param forceOverride Whether to apply the new schema even if it is incompatible. All
* incompatible documents will be deleted.
* @param userId Id of the calling user
@@ -44,11 +46,11 @@
in String databaseName,
in List<Bundle> schemaBundles,
in List<String> schemasNotPlatformSurfaceable,
+ in Map<String, List<Bundle>> schemasPackageAccessibleBundles,
boolean forceOverride,
in int userId,
in IAppSearchResultCallback callback);
-
/**
* Retrieves the AppSearch schema for this database.
*
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java b/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java
index 43be442..bfb9323 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java
@@ -17,16 +17,17 @@
package android.app.appsearch;
import android.annotation.NonNull;
+import android.app.appsearch.util.BundleUtil;
+import android.os.Bundle;
import com.android.internal.util.Preconditions;
-import java.util.Arrays;
-import java.util.Objects;
-
/** This class represents a uniquely identifiable package. */
public class PackageIdentifier {
- private final String mPackageName;
- private final byte[] mSha256Certificate;
+ private static final String PACKAGE_NAME_FIELD = "packageName";
+ private static final String SHA256_CERTIFICATE_FIELD = "sha256Certificate";
+
+ private final Bundle mBundle;
/**
* Creates a unique identifier for a package.
@@ -35,18 +36,30 @@
* @param sha256Certificate SHA256 certificate digest of the package.
*/
public PackageIdentifier(@NonNull String packageName, @NonNull byte[] sha256Certificate) {
- mPackageName = Preconditions.checkNotNull(packageName);
- mSha256Certificate = Preconditions.checkNotNull(sha256Certificate);
+ mBundle = new Bundle();
+ mBundle.putString(PACKAGE_NAME_FIELD, packageName);
+ mBundle.putByteArray(SHA256_CERTIFICATE_FIELD, sha256Certificate);
+ }
+
+ /** @hide */
+ public PackageIdentifier(@NonNull Bundle bundle) {
+ mBundle = Preconditions.checkNotNull(bundle);
+ }
+
+ /** @hide */
+ @NonNull
+ public Bundle getBundle() {
+ return mBundle;
}
@NonNull
public String getPackageName() {
- return mPackageName;
+ return Preconditions.checkNotNull(mBundle.getString(PACKAGE_NAME_FIELD));
}
@NonNull
public byte[] getSha256Certificate() {
- return mSha256Certificate;
+ return Preconditions.checkNotNull(mBundle.getByteArray(SHA256_CERTIFICATE_FIELD));
}
@Override
@@ -58,12 +71,11 @@
return false;
}
final PackageIdentifier other = (PackageIdentifier) obj;
- return this.mPackageName.equals(other.mPackageName)
- && Arrays.equals(this.mSha256Certificate, other.mSha256Certificate);
+ return BundleUtil.deepEquals(mBundle, other.mBundle);
}
@Override
public int hashCode() {
- return Objects.hash(mPackageName, Arrays.hashCode(mSha256Certificate));
+ return BundleUtil.deepHashCode(mBundle);
}
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
index b9503ee..0f141d6 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
@@ -30,7 +30,7 @@
/**
* Encapsulates a request to index a document into an {@link AppSearchSession} database.
*
- * @see AppSearchSession#putDocuments
+ * <p>@see AppSearchSession#putDocuments
*/
public final class PutDocumentsRequest {
private final List<GenericDocument> mDocuments;
@@ -45,12 +45,16 @@
return Collections.unmodifiableList(mDocuments);
}
- /** Builder for {@link PutDocumentsRequest} objects. */
+ /**
+ * Builder for {@link PutDocumentsRequest} objects.
+ *
+ * <p>Once {@link #build} is called, the instance can no longer be used.
+ */
public static final class Builder {
private final List<GenericDocument> mDocuments = new ArrayList<>();
private boolean mBuilt = false;
- /** Adds one or more documents to the request. */
+ /** Adds one or more {@link GenericDocument} objects to the request. */
@SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments()
@NonNull
public Builder addGenericDocument(@NonNull GenericDocument... documents) {
@@ -58,17 +62,18 @@
return addGenericDocument(Arrays.asList(documents));
}
- /** Adds one or more documents to the request. */
+ /** Adds a collection of {@link GenericDocument} objects to the request. */
@SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments()
@NonNull
- public Builder addGenericDocument(@NonNull Collection<GenericDocument> documents) {
+ public Builder addGenericDocument(
+ @NonNull Collection<? extends GenericDocument> documents) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(documents);
mDocuments.addAll(documents);
return this;
}
- /** Builds a new {@link PutDocumentsRequest}. */
+ /** Creates a new {@link PutDocumentsRequest} object. */
@NonNull
public PutDocumentsRequest build() {
Preconditions.checkState(!mBuilt, "Builder has already been used");
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 eb0b7324..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. */
@@ -109,10 +112,9 @@
}
/**
- * Contains the package name that stored the {@link GenericDocument}.
+ * Contains the package name of the app that stored the {@link GenericDocument}.
*
* @return Package name that stored the document
- * @hide
*/
@NonNull
public String getPackageName() {
@@ -120,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/framework/java/external/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
index c3f0d8a..b5d5f04d 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.app.appsearch.exceptions.IllegalSearchSpecException;
import android.os.Bundle;
import android.util.ArrayMap;
@@ -42,17 +43,15 @@
// TODO(sidchhabra) : AddResultSpec fields for Snippets etc.
public final class SearchSpec {
/**
- * Schema type to be used in {@link SearchSpec.Builder#addProjectionTypePropertyPath} to apply
- * property paths to all results, excepting any types that have had their own, specific property
- * paths set.
- *
- * @hide
+ * Schema type to be used in {@link SearchSpec.Builder#addProjection} to apply property paths to
+ * all results, excepting any types that have had their own, specific property paths set.
*/
public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
static final String TERM_MATCH_TYPE_FIELD = "termMatchType";
static final String SCHEMA_TYPE_FIELD = "schemaType";
static final String NAMESPACE_FIELD = "namespace";
+ static final String PACKAGE_NAME_FIELD = "packageName";
static final String NUM_PER_PAGE_FIELD = "numPerPage";
static final String RANKING_STRATEGY_FIELD = "rankingStrategy";
static final String ORDER_FIELD = "order";
@@ -106,7 +105,8 @@
value = {
RANKING_STRATEGY_NONE,
RANKING_STRATEGY_DOCUMENT_SCORE,
- RANKING_STRATEGY_CREATION_TIMESTAMP
+ RANKING_STRATEGY_CREATION_TIMESTAMP,
+ RANKING_STRATEGY_RELEVANCE_SCORE
})
@Retention(RetentionPolicy.SOURCE)
public @interface RankingStrategy {}
@@ -117,6 +117,8 @@
public static final int RANKING_STRATEGY_DOCUMENT_SCORE = 1;
/** Ranked by document creation timestamps. */
public static final int RANKING_STRATEGY_CREATION_TIMESTAMP = 2;
+ /** Ranked by document relevance score. */
+ public static final int RANKING_STRATEGY_RELEVANCE_SCORE = 3;
/**
* Order for query result.
@@ -172,7 +174,7 @@
}
/**
- * Returns the list of namespaces to search for.
+ * Returns the list of namespaces to search over.
*
* <p>If empty, the query will search over all namespaces.
*/
@@ -185,6 +187,40 @@
return Collections.unmodifiableList(namespaces);
}
+ /**
+ * Returns the list of package name filters to search over.
+ *
+ * <p>If empty, the query will search over all packages that the caller has access to. If
+ * package names are specified which caller doesn't have access to, then those package names
+ * will be ignored.
+ */
+ @NonNull
+ public List<String> getFilterPackageNames() {
+ List<String> packageNames = mBundle.getStringArrayList(PACKAGE_NAME_FIELD);
+ if (packageNames == null) {
+ return Collections.emptyList();
+ }
+ return Collections.unmodifiableList(packageNames);
+ }
+
+ /**
+ * Returns the list of package names to search over.
+ *
+ * <p>If unset, the query will search over all packages that the caller has access to. If
+ * package names are specified which caller doesn't have access to, then those package names
+ * will be ignored.
+ *
+ * @hide
+ */
+ @NonNull
+ public List<String> getPackageNames() {
+ List<String> packageNames = mBundle.getStringArrayList(PACKAGE_NAME_FIELD);
+ if (packageNames == null) {
+ return Collections.emptyList();
+ }
+ return Collections.unmodifiableList(packageNames);
+ }
+
/** Returns the number of results per page in the result set. */
public int getResultCountPerPage() {
return mBundle.getInt(NUM_PER_PAGE_FIELD, DEFAULT_NUM_PER_PAGE);
@@ -224,11 +260,9 @@
*
* <p>Calling this function repeatedly is inefficient. Prefer to retain the Map returned by this
* function, rather than calling it multiple times.
- *
- * @hide
*/
@NonNull
- public Map<String, List<String>> getProjectionTypePropertyPaths() {
+ public Map<String, List<String>> getProjections() {
Bundle typePropertyPathsBundle = mBundle.getBundle(PROJECTION_TYPE_PROPERTY_PATHS_FIELD);
Set<String> schemaTypes = typePropertyPathsBundle.keySet();
Map<String, List<String>> typePropertyPathsMap = new ArrayMap<>(schemaTypes.size());
@@ -245,6 +279,7 @@
private final Bundle mBundle;
private final ArrayList<String> mSchemaTypes = new ArrayList<>();
private final ArrayList<String> mNamespaces = new ArrayList<>();
+ private final ArrayList<String> mPackageNames = new ArrayList<>();
private final Bundle mProjectionTypePropertyMasks = new Bundle();
private boolean mBuilt = false;
@@ -319,6 +354,43 @@
}
/**
+ * Adds a package name filter to {@link SearchSpec} Entry. Only search for documents that
+ * were indexed from the specified packages.
+ *
+ * <p>If unset, the query will search over all packages that the caller has access to. If
+ * package names are specified which caller doesn't have access to, then those package names
+ * will be ignored.
+ */
+ // Getter is called "getFilterPackageNames" (as opposed to the suggested
+ // "getFilterPackageNameses")
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @NonNull
+ public Builder addFilterPackageNames(@NonNull String... packageNames) {
+ Preconditions.checkNotNull(packageNames);
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ return addFilterPackageNames(Arrays.asList(packageNames));
+ }
+
+ /**
+ * Adds a package name filter to {@link SearchSpec} Entry. Only search for documents that
+ * were indexed from the specified packages.
+ *
+ * <p>If unset, the query will search over all packages that the caller has access to. If
+ * package names are specified which caller doesn't have access to, then those package names
+ * will be ignored.
+ */
+ // Getter is called "getFilterPackageNames" (as opposed to the suggested
+ // "getFilterPackageNameses")
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @NonNull
+ public Builder addFilterPackageNames(@NonNull Collection<String> packageNames) {
+ Preconditions.checkNotNull(packageNames);
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mPackageNames.addAll(packageNames);
+ return this;
+ }
+
+ /**
* Sets the number of results per page in the returned object.
*
* <p>The default number of results per page is 10.
@@ -339,7 +411,7 @@
Preconditions.checkArgumentInRange(
rankingStrategy,
RANKING_STRATEGY_NONE,
- RANKING_STRATEGY_CREATION_TIMESTAMP,
+ RANKING_STRATEGY_RELEVANCE_SCORE,
"Result ranking strategy");
mBundle.putInt(RANKING_STRATEGY_FIELD, rankingStrategy);
return this;
@@ -480,14 +552,12 @@
* subject: "IMPORTANT"
* }
* }</pre>
- *
- * @hide
*/
@NonNull
- public SearchSpec.Builder addProjectionTypePropertyPaths(
+ public SearchSpec.Builder addProjection(
@NonNull String schemaType, @NonNull String... propertyPaths) {
Preconditions.checkNotNull(propertyPaths);
- return addProjectionTypePropertyPaths(schemaType, Arrays.asList(propertyPaths));
+ return addProjection(schemaType, Arrays.asList(propertyPaths));
}
/**
@@ -503,12 +573,10 @@
* then those property paths will apply to all results, excepting any types that have their
* own, specific property paths set.
*
- * <p>{@see SearchSpec.Builder#addProjectionTypePropertyPath(String, String...)}
- *
- * @hide
+ * <p>{@see SearchSpec.Builder#addProjection(String, String...)}
*/
@NonNull
- public SearchSpec.Builder addProjectionTypePropertyPaths(
+ public SearchSpec.Builder addProjection(
@NonNull String schemaType, @NonNull Collection<String> propertyPaths) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(schemaType);
@@ -535,6 +603,7 @@
}
mBundle.putStringArrayList(NAMESPACE_FIELD, mNamespaces);
mBundle.putStringArrayList(SCHEMA_TYPE_FIELD, mSchemaTypes);
+ mBundle.putStringArrayList(PACKAGE_NAME_FIELD, mPackageNames);
mBundle.putBundle(PROJECTION_TYPE_PROPERTY_PATHS_FIELD, mProjectionTypePropertyMasks);
mBuilt = true;
return new SearchSpec(mBundle);
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index 87c41e5..1f1e9a1 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -27,6 +27,7 @@
import android.app.appsearch.IAppSearchBatchResultCallback;
import android.app.appsearch.IAppSearchManager;
import android.app.appsearch.IAppSearchResultCallback;
+import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.SearchResultPage;
import android.app.appsearch.SearchSpec;
import android.content.Context;
@@ -34,6 +35,7 @@
import android.os.Bundle;
import android.os.ParcelableException;
import android.os.RemoteException;
+import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -42,6 +44,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* TODO(b/142567528): add comments when implement this class
@@ -64,6 +67,7 @@
@NonNull String databaseName,
@NonNull List<Bundle> schemaBundles,
@NonNull List<String> schemasNotPlatformSurfaceable,
+ @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles,
boolean forceOverride,
@UserIdInt int userId,
@NonNull IAppSearchResultCallback callback) {
@@ -78,9 +82,25 @@
for (int i = 0; i < schemaBundles.size(); i++) {
schemas.add(new AppSearchSchema(schemaBundles.get(i)));
}
+ Map<String, List<PackageIdentifier>> schemasPackageAccessible =
+ new ArrayMap<>(schemasPackageAccessibleBundles.size());
+ for (Map.Entry<String, List<Bundle>> entry :
+ schemasPackageAccessibleBundles.entrySet()) {
+ List<PackageIdentifier> packageIdentifiers =
+ new ArrayList<>(entry.getValue().size());
+ for (int i = 0; i < packageIdentifiers.size(); i++) {
+ packageIdentifiers.add(new PackageIdentifier(entry.getValue().get(i)));
+ }
+ schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
+ }
AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
String packageName = convertUidToPackageName(callingUid);
- impl.setSchema(packageName, databaseName, schemas, schemasNotPlatformSurfaceable,
+ impl.setSchema(
+ packageName,
+ databaseName,
+ schemas,
+ schemasNotPlatformSurfaceable,
+ schemasPackageAccessible,
forceOverride);
invokeCallbackOnResult(callback,
AppSearchResult.newSuccessfulResult(/*result=*/ null));
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index a2126b1..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
@@ -21,10 +21,12 @@
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.GenericDocument;
+import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.SearchResultPage;
import android.app.appsearch.SearchSpec;
import android.app.appsearch.exceptions.AppSearchException;
import android.os.Bundle;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -43,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;
@@ -60,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;
@@ -230,6 +234,7 @@
* @param schemas Schemas to set for this app.
* @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform
* surfaces.
+ * @param schemasPackageAccessible Schema types that are visible to the specified packages.
* @param forceOverride Whether to force-apply the schema even if it is incompatible. Documents
* which do not comply with the new schema will be deleted.
* @throws AppSearchException on IcingSearchEngine error.
@@ -239,6 +244,7 @@
@NonNull String databaseName,
@NonNull List<AppSearchSchema> schemas,
@NonNull List<String> schemasNotPlatformSurfaceable,
+ @NonNull Map<String, List<PackageIdentifier>> schemasPackageAccessible,
boolean forceOverride)
throws AppSearchException {
mReadWriteLock.writeLock().lock();
@@ -291,7 +297,18 @@
prefixedSchemasNotPlatformSurfaceable.add(
prefix + schemasNotPlatformSurfaceable.get(i));
}
- mVisibilityStoreLocked.setVisibility(prefix, prefixedSchemasNotPlatformSurfaceable);
+
+ Map<String, List<PackageIdentifier>> prefixedSchemasPackageAccessible =
+ new ArrayMap<>(schemasNotPlatformSurfaceable.size());
+ for (Map.Entry<String, List<PackageIdentifier>> entry :
+ schemasPackageAccessible.entrySet()) {
+ prefixedSchemasPackageAccessible.put(prefix + entry.getKey(), entry.getValue());
+ }
+
+ mVisibilityStoreLocked.setVisibility(
+ prefix,
+ prefixedSchemasNotPlatformSurfaceable,
+ prefixedSchemasPackageAccessible);
// Determine whether to schedule an immediate optimize.
if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
@@ -417,7 +434,9 @@
try {
getResultProto =
mIcingSearchEngineLocked.get(
- createPrefix(packageName, databaseName) + namespace, uri);
+ createPrefix(packageName, databaseName) + namespace,
+ uri,
+ GetResultSpecProto.getDefaultInstance());
} finally {
mReadWriteLock.readLock().unlock();
}
@@ -448,6 +467,13 @@
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec)
throws AppSearchException {
+ if (!searchSpec.getPackageNames().isEmpty()
+ && !searchSpec.getPackageNames().contains(packageName)) {
+ // Client wanted to query over some packages that weren't its own. This isn't
+ // allowed through local query so we can return early with no results.
+ return new SearchResultPage(Bundle.EMPTY);
+ }
+
mReadWriteLock.readLock().lock();
try {
return doQueryLocked(
@@ -480,13 +506,25 @@
// verified.
mReadWriteLock.readLock().lock();
try {
- // We use the mNamespaceMap.keySet here because it's the smaller set of valid prefixes
- // that could exist.
- Set<String> prefixes = mNamespaceMapLocked.keySet();
+ Set<String> prefixes = new ArraySet<>();
+ Set<String> packageFilters = new ArraySet<>(searchSpec.getPackageNames());
- // Filter out any VisibilityStore documents which are AppSearch-internal only.
- prefixes.remove(
- createPrefix(VisibilityStore.PACKAGE_NAME, VisibilityStore.DATABASE_NAME));
+ for (String prefix : mNamespaceMapLocked.keySet()) {
+ if (prefix.equals(VisibilityStore.VISIBILITY_STORE_PREFIX)) {
+ // Filter out any VisibilityStore documents which are AppSearch-internal only.
+ continue;
+ }
+
+ if (!packageFilters.isEmpty() && !packageFilters.contains(getPackageName(prefix))) {
+ // Client wanted to restrict search over specified packages. Since the
+ // specified packages don't include this prefix, don't add it to our search
+ // filters.
+ continue;
+ }
+
+ // Otherwise, include this prefix in our global search.
+ prefixes.add(prefix);
+ }
return doQueryLocked(prefixes, queryExpression, searchSpec);
} finally {
@@ -500,22 +538,30 @@
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec)
throws AppSearchException {
- SearchSpecProto searchSpecProto = SearchSpecToProtoConverter.toSearchSpecProto(searchSpec);
SearchSpecProto.Builder searchSpecBuilder =
- searchSpecProto.toBuilder().setQuery(queryExpression);
-
- ResultSpecProto resultSpec = SearchSpecToProtoConverter.toResultSpecProto(searchSpec);
- ScoringSpecProto scoringSpec = SearchSpecToProtoConverter.toScoringSpecProto(searchSpec);
- SearchResultProto searchResultProto;
-
+ SearchSpecToProtoConverter.toSearchSpecProto(searchSpec).toBuilder()
+ .setQuery(queryExpression);
// rewriteSearchSpecForPrefixesLocked will return false if none of the prefixes that the
// client is trying to search on exist, so we can return an empty SearchResult and skip
// sending request to Icing.
if (!rewriteSearchSpecForPrefixesLocked(searchSpecBuilder, prefixes)) {
return new SearchResultPage(Bundle.EMPTY);
}
- searchResultProto =
- mIcingSearchEngineLocked.search(searchSpecBuilder.build(), scoringSpec, resultSpec);
+
+ ResultSpecProto.Builder resultSpecBuilder =
+ SearchSpecToProtoConverter.toResultSpecProto(searchSpec).toBuilder();
+
+ // rewriteResultSpecForPrefixesLocked will return false if none of the prefixes that the
+ // client is trying to search on exist, so we can return an empty SearchResult and skip
+ // sending request to Icing.
+ if (!rewriteResultSpecForPrefixesLocked(resultSpecBuilder, prefixes)) {
+ return new SearchResultPage(Bundle.EMPTY);
+ }
+
+ ScoringSpecProto scoringSpec = SearchSpecToProtoConverter.toScoringSpecProto(searchSpec);
+ SearchResultProto searchResultProto =
+ mIcingSearchEngineLocked.search(
+ searchSpecBuilder.build(), scoringSpec, resultSpecBuilder.build());
checkSuccess(searchResultProto.getStatus());
return rewriteSearchResultProto(searchResultProto);
@@ -607,6 +653,14 @@
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec)
throws AppSearchException {
+ if (!searchSpec.getPackageNames().isEmpty()
+ && !searchSpec.getPackageNames().contains(packageName)) {
+ // We're only removing documents within the parameter `packageName`. If we're not
+ // restricting our remove-query to this package name, then there's nothing for us to
+ // remove.
+ return;
+ }
+
SearchSpecProto searchSpecProto = SearchSpecToProtoConverter.toSearchSpecProto(searchSpec);
SearchSpecProto.Builder searchSpecBuilder =
searchSpecProto.toBuilder().setQuery(queryExpression);
@@ -641,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 =
@@ -915,6 +967,46 @@
return true;
}
+ /**
+ * Rewrites the typePropertyMasks that exist in {@code prefixes}.
+ *
+ * <p>This method should be only called in query methods and get the READ lock to keep thread
+ * safety.
+ *
+ * @return false if none of the requested prefixes exist.
+ */
+ @VisibleForTesting
+ @GuardedBy("mReadWriteLock")
+ boolean rewriteResultSpecForPrefixesLocked(
+ @NonNull ResultSpecProto.Builder resultSpecBuilder, @NonNull Set<String> prefixes) {
+ // Create a copy since retainAll() modifies the original set.
+ Set<String> existingPrefixes = new ArraySet<>(mNamespaceMapLocked.keySet());
+ existingPrefixes.retainAll(prefixes);
+
+ if (existingPrefixes.isEmpty()) {
+ // None of the prefixes exist, empty query.
+ return false;
+ }
+
+ 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 (TypePropertyMask typePropertyMask : resultSpecBuilder.getTypePropertyMasksList()) {
+ String qualifiedType = prefix + typePropertyMask.getSchemaType();
+ if (existingSchemaTypes.contains(qualifiedType)) {
+ prefixedTypePropertyMasks.add(
+ typePropertyMask.toBuilder().setSchemaType(qualifiedType).build());
+ }
+ }
+ }
+ resultSpecBuilder
+ .clearTypePropertyMasks()
+ .addAllTypePropertyMasks(prefixedTypePropertyMasks);
+ return true;
+ }
+
@VisibleForTesting
@GuardedBy("mReadWriteLock")
SchemaProto getSchemaProtoLocked() throws AppSearchException {
@@ -969,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
@@ -1087,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 =
@@ -1094,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/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java
index 7e4ebb5..a940ec1 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java
@@ -20,6 +20,7 @@
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.GenericDocument;
+import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.exceptions.AppSearchException;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -27,19 +28,21 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.Set;
/**
- * Manages any visibility settings for all the databases that AppSearchImpl knows about. Persists
- * the visibility settings and reloads them on initialization.
+ * Manages any visibility settings for all the package's databases that AppSearchImpl knows about.
+ * Persists the visibility settings and reloads them on initialization.
*
- * <p>The VisibilityStore creates a document for each database. This document holds the visibility
- * settings that apply to that database. The VisibilityStore also creates a schema for these
- * documents and has its own database so that its data doesn't interfere with any clients' data. It
- * persists the document and schema through AppSearchImpl.
+ * <p>The VisibilityStore creates a document for each package's databases. This document holds the
+ * visibility settings that apply to that package's database. The VisibilityStore also creates a
+ * schema for these documents and has its own package and database so that its data doesn't
+ * interfere with any clients' data. It persists the document and schema through AppSearchImpl.
*
* <p>These visibility settings are used to ensure AppSearch queries respect the clients' settings
* on who their data is visible to.
@@ -52,18 +55,31 @@
*/
class VisibilityStore {
/** Schema type for documents that hold AppSearch's metadata, e.g. visibility settings */
- @VisibleForTesting static final String SCHEMA_TYPE = "Visibility";
+ @VisibleForTesting static final String VISIBILITY_TYPE = "VisibilityType";
/**
* Property that holds the list of platform-hidden schemas, as part of the visibility settings.
*/
- @VisibleForTesting
- static final String NOT_PLATFORM_SURFACEABLE_PROPERTY = "notPlatformSurfaceable";
+ private static final String NOT_PLATFORM_SURFACEABLE_PROPERTY = "notPlatformSurfaceable";
- /** Schema for the VisibilityStore's docuemnts. */
- @VisibleForTesting
- static final AppSearchSchema SCHEMA =
- new AppSearchSchema.Builder(SCHEMA_TYPE)
+ /** Property that holds nested documents of package accessible schemas. */
+ private static final String PACKAGE_ACCESSIBLE_PROPERTY = "packageAccessible";
+
+ /** Schema type for nested documents that hold package accessible information. */
+ private static final String PACKAGE_ACCESSIBLE_TYPE = "PackageAccessibleType";
+
+ /** Property that holds the package name that can access a schema. */
+ private static final String PACKAGE_NAME_PROPERTY = "packageName";
+
+ /** Property that holds the SHA 256 certificate of the app that can access a schema. */
+ private static final String SHA_256_CERT_PROPERTY = "sha256Cert";
+
+ /** Property that holds the prefixed schema type that is accessible by some package. */
+ private static final String ACCESSIBLE_SCHEMA_PROPERTY = "accessibleSchema";
+
+ /** Schema for the VisibilityStore's documents. */
+ private static final AppSearchSchema VISIBILITY_SCHEMA =
+ new AppSearchSchema.Builder(VISIBILITY_TYPE)
.addProperty(
new AppSearchSchema.PropertyConfig.Builder(
NOT_PLATFORM_SURFACEABLE_PROPERTY)
@@ -71,6 +87,39 @@
.setCardinality(
AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
.build())
+ .addProperty(
+ new AppSearchSchema.PropertyConfig.Builder(PACKAGE_ACCESSIBLE_PROPERTY)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT)
+ .setSchemaType(PACKAGE_ACCESSIBLE_TYPE)
+ .setCardinality(
+ AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+ .build())
+ .build();
+
+ /**
+ * Schema for package accessible documents, these will be nested in a top-level visibility
+ * document.
+ */
+ private static final AppSearchSchema PACKAGE_ACCESSIBLE_SCHEMA =
+ new AppSearchSchema.Builder(PACKAGE_ACCESSIBLE_TYPE)
+ .addProperty(
+ new AppSearchSchema.PropertyConfig.Builder(PACKAGE_NAME_PROPERTY)
+ .setCardinality(
+ AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+ .build())
+ .addProperty(
+ new AppSearchSchema.PropertyConfig.Builder(SHA_256_CERT_PROPERTY)
+ .setCardinality(
+ AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
+ .build())
+ .addProperty(
+ new AppSearchSchema.PropertyConfig.Builder(ACCESSIBLE_SCHEMA_PROPERTY)
+ .setCardinality(
+ AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+ .build())
.build();
/**
@@ -86,7 +135,7 @@
* database name. Tracked here to tell when we're looking at our own prefix when looking through
* AppSearchImpl.
*/
- private static final String VISIBILITY_STORE_PREFIX =
+ static final String VISIBILITY_STORE_PREFIX =
AppSearchImpl.createPrefix(PACKAGE_NAME, DATABASE_NAME);
/** Namespace of documents that contain visibility settings */
@@ -102,10 +151,23 @@
/**
* Maps prefixes to the set of schemas that are platform-hidden within that prefix. All schemas
* in the map are prefixed.
+ *
+ * <p>Although the prefix key isn't used for lookup, it's helpful in ensuring that all previous
+ * visibility settings for a prefix are completely overridden by new visibility settings.
*/
private final Map<String, Set<String>> mNotPlatformSurfaceableMap = new ArrayMap<>();
/**
+ * Maps prefixes to a an internal map. The internal map maps prefixed schemas to the set of
+ * PackageIdentifiers that have access to that schema.
+ *
+ * <p>Although the prefix key isn't used for lookup, it's helpful in ensuring that all previous
+ * visibility settings for a prefix are completely overridden by new visibility settings.
+ */
+ private final Map<String, Map<String, Set<PackageIdentifier>>> mPackageAccessibleMap =
+ new ArrayMap<>();
+
+ /**
* Creates an uninitialized VisibilityStore object. Callers must also call {@link #initialize()}
* before using the object.
*
@@ -120,19 +182,22 @@
*
* <p>This is kept separate from the constructor because this will call methods on
* AppSearchImpl. Some may even then recursively call back into VisibilityStore (for example,
- * {@link AppSearchImpl#setSchema} will call {@link #setVisibility(String, Set)}. We need to
- * have both AppSearchImpl and VisibilityStore fully initialized for this call flow to work.
+ * {@link AppSearchImpl#setSchema} will call {@link #setVisibility}. We need to have both
+ * AppSearchImpl and VisibilityStore fully initialized for this call flow to work.
*
* @throws AppSearchException AppSearchException on AppSearchImpl error.
*/
public void initialize() throws AppSearchException {
- if (!mAppSearchImpl.hasSchemaTypeLocked(PACKAGE_NAME, DATABASE_NAME, SCHEMA_TYPE)) {
+ if (!mAppSearchImpl.hasSchemaTypeLocked(PACKAGE_NAME, DATABASE_NAME, VISIBILITY_TYPE)
+ || !mAppSearchImpl.hasSchemaTypeLocked(
+ PACKAGE_NAME, DATABASE_NAME, PACKAGE_ACCESSIBLE_TYPE)) {
// Schema type doesn't exist yet. Add it.
mAppSearchImpl.setSchema(
PACKAGE_NAME,
DATABASE_NAME,
- Collections.singletonList(SCHEMA),
+ Arrays.asList(VISIBILITY_SCHEMA, PACKAGE_ACCESSIBLE_SCHEMA),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
}
@@ -153,9 +218,41 @@
NAMESPACE,
/*uri=*/ addUriPrefix(prefix));
+ // Update platform visibility settings
String[] schemas =
document.getPropertyStringArray(NOT_PLATFORM_SURFACEABLE_PROPERTY);
- mNotPlatformSurfaceableMap.put(prefix, new ArraySet<>(Arrays.asList(schemas)));
+ if (schemas != null) {
+ mNotPlatformSurfaceableMap.put(prefix, new ArraySet<>(Arrays.asList(schemas)));
+ }
+
+ // Update 3p package visibility settings
+ Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap = new ArrayMap<>();
+ GenericDocument[] packageAccessibleDocuments =
+ document.getPropertyDocumentArray(PACKAGE_ACCESSIBLE_PROPERTY);
+ if (packageAccessibleDocuments != null) {
+ for (int i = 0; i < packageAccessibleDocuments.length; i++) {
+ String packageName =
+ packageAccessibleDocuments[i].getPropertyString(
+ PACKAGE_NAME_PROPERTY);
+ byte[] sha256Cert =
+ packageAccessibleDocuments[i].getPropertyBytes(
+ SHA_256_CERT_PROPERTY);
+ PackageIdentifier packageIdentifier =
+ new PackageIdentifier(packageName, sha256Cert);
+
+ String prefixedSchema =
+ packageAccessibleDocuments[i].getPropertyString(
+ ACCESSIBLE_SCHEMA_PROPERTY);
+ Set<PackageIdentifier> packageIdentifiers =
+ schemaToPackageIdentifierMap.get(prefixedSchema);
+ if (packageIdentifiers == null) {
+ packageIdentifiers = new ArraySet<>();
+ }
+ packageIdentifiers.add(packageIdentifier);
+ schemaToPackageIdentifierMap.put(prefixedSchema, packageIdentifiers);
+ }
+ }
+ mPackageAccessibleMap.put(prefix, schemaToPackageIdentifierMap);
} catch (AppSearchException e) {
if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) {
// TODO(b/172068212): This indicates some desync error. We were expecting a
@@ -176,31 +273,67 @@
* @param prefix Prefix that identifies who owns the {@code schemasNotPlatformSurfaceable}.
* @param schemasNotPlatformSurfaceable Set of prefixed schemas that should be hidden from the
* platform.
+ * @param schemasPackageAccessible Map of prefixed schemas to a list of package identifiers that
+ * have access to the schema.
* @throws AppSearchException on AppSearchImpl error.
*/
public void setVisibility(
- @NonNull String prefix, @NonNull Set<String> schemasNotPlatformSurfaceable)
+ @NonNull String prefix,
+ @NonNull Set<String> schemasNotPlatformSurfaceable,
+ @NonNull Map<String, List<PackageIdentifier>> schemasPackageAccessible)
throws AppSearchException {
Preconditions.checkNotNull(prefix);
Preconditions.checkNotNull(schemasNotPlatformSurfaceable);
+ Preconditions.checkNotNull(schemasPackageAccessible);
// Persist the document
GenericDocument.Builder visibilityDocument =
- new GenericDocument.Builder(/*uri=*/ addUriPrefix(prefix), SCHEMA_TYPE)
+ new GenericDocument.Builder(/*uri=*/ addUriPrefix(prefix), VISIBILITY_TYPE)
.setNamespace(NAMESPACE);
if (!schemasNotPlatformSurfaceable.isEmpty()) {
visibilityDocument.setPropertyString(
NOT_PLATFORM_SURFACEABLE_PROPERTY,
schemasNotPlatformSurfaceable.toArray(new String[0]));
}
+
+ Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap = new ArrayMap<>();
+ List<GenericDocument> packageAccessibleDocuments = new ArrayList<>();
+ for (Map.Entry<String, List<PackageIdentifier>> entry :
+ schemasPackageAccessible.entrySet()) {
+ for (int i = 0; i < entry.getValue().size(); i++) {
+ // TODO(b/169883602): remove the "placeholder" uri once upstream changes to relax
+ // nested
+ // document uri rules gets synced down.
+ GenericDocument packageAccessibleDocument =
+ new GenericDocument.Builder(/*uri=*/ "placeholder", PACKAGE_ACCESSIBLE_TYPE)
+ .setNamespace(NAMESPACE)
+ .setPropertyString(
+ PACKAGE_NAME_PROPERTY,
+ entry.getValue().get(i).getPackageName())
+ .setPropertyBytes(
+ SHA_256_CERT_PROPERTY,
+ entry.getValue().get(i).getSha256Certificate())
+ .setPropertyString(ACCESSIBLE_SCHEMA_PROPERTY, entry.getKey())
+ .build();
+ packageAccessibleDocuments.add(packageAccessibleDocument);
+ }
+ schemaToPackageIdentifierMap.put(entry.getKey(), new ArraySet<>(entry.getValue()));
+ }
+ if (!packageAccessibleDocuments.isEmpty()) {
+ visibilityDocument.setPropertyDocument(
+ PACKAGE_ACCESSIBLE_PROPERTY,
+ packageAccessibleDocuments.toArray(new GenericDocument[0]));
+ }
+
mAppSearchImpl.putDocument(PACKAGE_NAME, DATABASE_NAME, visibilityDocument.build());
// Update derived data structures.
mNotPlatformSurfaceableMap.put(prefix, schemasNotPlatformSurfaceable);
+ mPackageAccessibleMap.put(prefix, schemaToPackageIdentifierMap);
}
/** Returns if the schema is surfaceable by the platform. */
- @NonNull
+ // TODO(b/169883602): check permissions against the allowlisted global querier package name.
public boolean isSchemaPlatformSurfaceable(
@NonNull String prefix, @NonNull String prefixedSchema) {
Preconditions.checkNotNull(prefix);
@@ -212,6 +345,31 @@
return !notPlatformSurfaceableSchemas.contains(prefixedSchema);
}
+ /** Returns whether the schema is accessible by {@code accessingPackage}. */
+ // TODO(b/169883602): check certificate and package against the incoming querier's uid/package.
+ public boolean isSchemaPackageAccessible(
+ @NonNull String prefix,
+ @NonNull String prefixedSchema,
+ @NonNull PackageIdentifier accessingPackage) {
+ Preconditions.checkNotNull(prefix);
+ Preconditions.checkNotNull(prefixedSchema);
+ Preconditions.checkNotNull(accessingPackage);
+
+ Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap =
+ mPackageAccessibleMap.get(prefix);
+ if (schemaToPackageIdentifierMap == null) {
+ return false;
+ }
+
+ Set<PackageIdentifier> packageIdentifiers =
+ schemaToPackageIdentifierMap.get(prefixedSchema);
+ if (packageIdentifiers == null) {
+ return false;
+ }
+
+ return packageIdentifiers.contains(accessingPackage);
+ }
+
/**
* Handles an {@code AppSearchImpl#reset()} by clearing any cached state.
*
@@ -219,6 +377,7 @@
*/
void handleReset() {
mNotPlatformSurfaceableMap.clear();
+ mPackageAccessibleMap.clear();
}
/**
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 814ee4f..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,10 @@
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;
/**
* Translates a {@link SearchSpec} into icing search protos.
@@ -57,14 +61,22 @@
@NonNull
public static ResultSpecProto toResultSpecProto(@NonNull SearchSpec spec) {
Preconditions.checkNotNull(spec);
- return ResultSpecProto.newBuilder()
- .setNumPerPage(spec.getResultCountPerPage())
- .setSnippetSpec(
- ResultSpecProto.SnippetSpecProto.newBuilder()
- .setNumToSnippet(spec.getSnippetCount())
- .setNumMatchesPerProperty(spec.getSnippetCountPerProperty())
- .setMaxWindowBytes(spec.getMaxSnippetSize()))
- .build();
+ ResultSpecProto.Builder builder =
+ ResultSpecProto.newBuilder()
+ .setNumPerPage(spec.getResultCountPerPage())
+ .setSnippetSpec(
+ ResultSpecProto.SnippetSpecProto.newBuilder()
+ .setNumToSnippet(spec.getSnippetCount())
+ .setNumMatchesPerProperty(spec.getSnippetCountPerProperty())
+ .setMaxWindowBytes(spec.getMaxSnippetSize()));
+ Map<String, List<String>> projectionTypePropertyPaths = spec.getProjections();
+ for (Map.Entry<String, List<String>> e : projectionTypePropertyPaths.entrySet()) {
+ builder.addTypePropertyMasks(
+ TypePropertyMask.newBuilder()
+ .setSchemaType(e.getKey())
+ .addAllPaths(e.getValue()));
+ }
+ return builder.build();
}
/** Extracts {@link ScoringSpecProto} information from a {@link SearchSpec}. */
@@ -79,17 +91,27 @@
if (orderCodeProto == null) {
throw new IllegalArgumentException("Invalid result ranking order: " + orderCode);
}
- protoBuilder.setOrderBy(orderCodeProto);
-
- @SearchSpec.RankingStrategy int rankingStrategyCode = spec.getRankingStrategy();
- ScoringSpecProto.RankingStrategy.Code rankingStrategyCodeProto =
- ScoringSpecProto.RankingStrategy.Code.forNumber(rankingStrategyCode);
- if (rankingStrategyCodeProto == null) {
- throw new IllegalArgumentException(
- "Invalid result ranking strategy: " + rankingStrategyCode);
- }
- protoBuilder.setRankBy(rankingStrategyCodeProto);
+ protoBuilder
+ .setOrderBy(orderCodeProto)
+ .setRankBy(toProtoRankingStrategy(spec.getRankingStrategy()));
return protoBuilder.build();
}
+
+ private static ScoringSpecProto.RankingStrategy.Code toProtoRankingStrategy(
+ @SearchSpec.RankingStrategy int rankingStrategyCode) {
+ switch (rankingStrategyCode) {
+ case SearchSpec.RANKING_STRATEGY_NONE:
+ return ScoringSpecProto.RankingStrategy.Code.NONE;
+ case SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE:
+ return ScoringSpecProto.RankingStrategy.Code.DOCUMENT_SCORE;
+ case SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP:
+ return ScoringSpecProto.RankingStrategy.Code.CREATION_TIMESTAMP;
+ case SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE:
+ 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 73f64dc..9be3049 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-I8b7425b3f87153547d1c8f5b560be5a54c9be97e
+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/config/hiddenapi-temp-blocklist.txt b/config/hiddenapi-max-target-r-loprio.txt
similarity index 100%
rename from config/hiddenapi-temp-blocklist.txt
rename to config/hiddenapi-max-target-r-loprio.txt
diff --git a/core/api/current.txt b/core/api/current.txt
index 6913328..ba49b67 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -393,6 +393,7 @@
field public static final int calendarViewShown = 16843596; // 0x101034c
field public static final int calendarViewStyle = 16843613; // 0x101035d
field public static final int canControlMagnification = 16844039; // 0x1010507
+ field public static final int canPauseRecording = 16844314; // 0x101061a
field public static final int canPerformGestures = 16844045; // 0x101050d
field public static final int canRecord = 16844060; // 0x101051c
field @Deprecated public static final int canRequestEnhancedWebAccessibility = 16843736; // 0x10103d8
@@ -1054,9 +1055,11 @@
field public static final int parentActivityName = 16843687; // 0x10103a7
field @Deprecated public static final int password = 16843100; // 0x101015c
field public static final int path = 16842794; // 0x101002a
+ field public static final int pathAdvancedPattern = 16844319; // 0x101061f
field public static final int pathData = 16843781; // 0x1010405
field public static final int pathPattern = 16842796; // 0x101002c
field public static final int pathPrefix = 16842795; // 0x101002b
+ field public static final int pathSuffix = 16844317; // 0x101061d
field public static final int patternPathData = 16843978; // 0x10104ca
field public static final int permission = 16842758; // 0x1010006
field public static final int permissionFlags = 16843719; // 0x10103c7
@@ -1149,7 +1152,7 @@
field public static final int reqNavigation = 16843306; // 0x101022a
field public static final int reqTouchScreen = 16843303; // 0x1010227
field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603
- field public static final int requireDeviceScreenOn = 16844315; // 0x101061b
+ field public static final int requireDeviceScreenOn = 16844316; // 0x101061c
field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
field public static final int required = 16843406; // 0x101028e
field public static final int requiredAccountType = 16843734; // 0x10103d6
@@ -1290,8 +1293,10 @@
field public static final int spotShadowAlpha = 16843967; // 0x10104bf
field public static final int src = 16843033; // 0x1010119
field public static final int ssp = 16843747; // 0x10103e3
+ field public static final int sspAdvancedPattern = 16844320; // 0x1010620
field public static final int sspPattern = 16843749; // 0x10103e5
field public static final int sspPrefix = 16843748; // 0x10103e4
+ field public static final int sspSuffix = 16844318; // 0x101061e
field public static final int stackFromBottom = 16843005; // 0x10100fd
field public static final int stackViewStyle = 16843838; // 0x101043e
field public static final int starStyle = 16842882; // 0x1010082
@@ -1686,6 +1691,30 @@
field @Deprecated public static final int secondary_text_dark_nodisable = 17170438; // 0x1060006
field @Deprecated public static final int secondary_text_light = 17170439; // 0x1060007
field @Deprecated public static final int secondary_text_light_nodisable = 17170440; // 0x1060008
+ field public static final int system_accent_0 = 17170473; // 0x1060029
+ field public static final int system_accent_100 = 17170475; // 0x106002b
+ field public static final int system_accent_1000 = 17170484; // 0x1060034
+ field public static final int system_accent_200 = 17170476; // 0x106002c
+ field public static final int system_accent_300 = 17170477; // 0x106002d
+ field public static final int system_accent_400 = 17170478; // 0x106002e
+ field public static final int system_accent_50 = 17170474; // 0x106002a
+ field public static final int system_accent_500 = 17170479; // 0x106002f
+ field public static final int system_accent_600 = 17170480; // 0x1060030
+ field public static final int system_accent_700 = 17170481; // 0x1060031
+ field public static final int system_accent_800 = 17170482; // 0x1060032
+ field public static final int system_accent_900 = 17170483; // 0x1060033
+ field public static final int system_main_0 = 17170461; // 0x106001d
+ field public static final int system_main_100 = 17170463; // 0x106001f
+ field public static final int system_main_1000 = 17170472; // 0x1060028
+ field public static final int system_main_200 = 17170464; // 0x1060020
+ field public static final int system_main_300 = 17170465; // 0x1060021
+ field public static final int system_main_400 = 17170466; // 0x1060022
+ field public static final int system_main_50 = 17170462; // 0x106001e
+ field public static final int system_main_500 = 17170467; // 0x1060023
+ field public static final int system_main_600 = 17170468; // 0x1060024
+ field public static final int system_main_700 = 17170469; // 0x1060025
+ field public static final int system_main_800 = 17170470; // 0x1060026
+ field public static final int system_main_900 = 17170471; // 0x1060027
field public static final int tab_indicator_text = 17170441; // 0x1060009
field @Deprecated public static final int tertiary_text_dark = 17170448; // 0x1060010
field @Deprecated public static final int tertiary_text_light = 17170449; // 0x1060011
@@ -6731,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);
@@ -8134,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;
}
@@ -10321,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";
@@ -10808,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";
@@ -12020,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);
@@ -12034,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;
}
@@ -15514,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);
@@ -24723,6 +24755,7 @@
}
public final class TvInputInfo implements android.os.Parcelable {
+ method public boolean canPauseRecording();
method public boolean canRecord();
method @Deprecated public android.content.Intent createSettingsIntent();
method public android.content.Intent createSetupIntent();
@@ -24756,6 +24789,7 @@
public static final class TvInputInfo.Builder {
ctor public TvInputInfo.Builder(android.content.Context, android.content.ComponentName);
method public android.media.tv.TvInputInfo build();
+ method @NonNull public android.media.tv.TvInputInfo.Builder setCanPauseRecording(boolean);
method public android.media.tv.TvInputInfo.Builder setCanRecord(boolean);
method public android.media.tv.TvInputInfo.Builder setExtras(android.os.Bundle);
method public android.media.tv.TvInputInfo.Builder setTunerCount(int);
@@ -24847,7 +24881,9 @@
method public void notifyRecordingStopped(android.net.Uri);
method public void notifyTuned(android.net.Uri);
method public void onAppPrivateCommand(@NonNull String, android.os.Bundle);
+ method public void onPauseRecording(@NonNull android.os.Bundle);
method public abstract void onRelease();
+ method public void onResumeRecording(@NonNull android.os.Bundle);
method public abstract void onStartRecording(@Nullable android.net.Uri);
method public void onStartRecording(@Nullable android.net.Uri, @NonNull android.os.Bundle);
method public abstract void onStopRecording();
@@ -24897,7 +24933,11 @@
public class TvRecordingClient {
ctor public TvRecordingClient(android.content.Context, String, @NonNull android.media.tv.TvRecordingClient.RecordingCallback, android.os.Handler);
+ method public void pauseRecording();
+ method public void pauseRecording(@NonNull android.os.Bundle);
method public void release();
+ method public void resumeRecording();
+ method public void resumeRecording(@NonNull android.os.Bundle);
method public void sendAppPrivateCommand(@NonNull String, android.os.Bundle);
method public void startRecording(@Nullable android.net.Uri);
method public void startRecording(@Nullable android.net.Uri, @NonNull android.os.Bundle);
@@ -30045,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();
@@ -30943,6 +31001,7 @@
field public static final int PATTERN_LITERAL = 0; // 0x0
field public static final int PATTERN_PREFIX = 1; // 0x1
field public static final int PATTERN_SIMPLE_GLOB = 2; // 0x2
+ field public static final int PATTERN_SUFFIX = 4; // 0x4
}
public final class PersistableBundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -34433,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";
@@ -39726,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
@@ -39737,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
@@ -39766,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 {
@@ -40144,6 +40208,11 @@
field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int";
}
+ public static final class CarrierConfigManager.ImsServiceEntitlement {
+ field public static final String KEY_AES_URL_STRING = "imsserviceentitlement.aes_url_string";
+ field public static final String KEY_PREFIX = "imsserviceentitlement.";
+ }
+
public static final class CarrierConfigManager.Iwlan {
field public static final int AUTHENTICATION_METHOD_CERT = 1; // 0x1
field public static final int AUTHENTICATION_METHOD_EAP_ONLY = 0; // 0x0
@@ -41081,12 +41150,13 @@
public class PhoneNumberUtils {
ctor public PhoneNumberUtils();
method public static void addTtsSpan(android.text.Spannable, int, int);
+ method public static boolean areSamePhoneNumber(@NonNull String, @NonNull String, @NonNull String);
method @Deprecated public static String calledPartyBCDFragmentToString(byte[], int, int);
method public static String calledPartyBCDFragmentToString(byte[], int, int, int);
method @Deprecated public static String calledPartyBCDToString(byte[], int, int);
method public static String calledPartyBCDToString(byte[], int, int, int);
- method public static boolean compare(String, String);
- method public static boolean compare(android.content.Context, String, String);
+ method @Deprecated public static boolean compare(String, String);
+ method @Deprecated public static boolean compare(android.content.Context, String, String);
method public static String convertKeypadLettersToDigits(String);
method public static android.text.style.TtsSpan createTtsSpan(String);
method public static CharSequence createTtsSpannable(CharSequence);
@@ -41271,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
}
@@ -41364,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);
@@ -41821,8 +41944,8 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void switchMultiSimConfig(int);
method public void unregisterPhoneStateListener(@NonNull android.telephony.PhoneStateListener);
method public void updateAvailableNetworks(@NonNull java.util.List<android.telephony.AvailableNetworkInfo>, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
- method public void uploadCallComposerPicture(@NonNull java.nio.file.Path, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.ParcelUuid,android.telephony.TelephonyManager.CallComposerException>);
- method public void uploadCallComposerPicture(@NonNull java.io.InputStream, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.ParcelUuid,android.telephony.TelephonyManager.CallComposerException>);
+ method public void uploadCallComposerPicture(@NonNull java.nio.file.Path, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.ParcelUuid,android.telephony.TelephonyManager.CallComposerException>);
+ method public void uploadCallComposerPicture(@NonNull java.io.InputStream, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.ParcelUuid,android.telephony.TelephonyManager.CallComposerException>);
field public static final String ACTION_CARRIER_MESSAGING_CLIENT_SERVICE = "android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE";
field public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = "android.telephony.action.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE";
field public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE = "android.telephony.action.CARRIER_SIGNAL_PCO_VALUE";
@@ -41848,6 +41971,7 @@
field public static final int AUTHTYPE_EAP_SIM = 128; // 0x80
field public static final int CALL_COMPOSER_STATUS_OFF = 0; // 0x0
field public static final int CALL_COMPOSER_STATUS_ON = 1; // 0x1
+ field public static final int CALL_COMPOSER_STATUS_ON_NO_PICTURES = 2; // 0x2
field public static final int CALL_STATE_IDLE = 0; // 0x0
field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
field public static final int CALL_STATE_RINGING = 1; // 0x1
@@ -41973,6 +42097,7 @@
field public static final int ERROR_FILE_TOO_LARGE = 2; // 0x2
field public static final int ERROR_INPUT_CLOSED = 4; // 0x4
field public static final int ERROR_IO_EXCEPTION = 5; // 0x5
+ field public static final int ERROR_NETWORK_UNAVAILABLE = 6; // 0x6
field public static final int ERROR_REMOTE_END_CLOSED = 1; // 0x1
field public static final int ERROR_UNKNOWN = 0; // 0x0
}
@@ -49273,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
}
@@ -51637,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 141a78f..d91ea2c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -242,6 +242,7 @@
field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
field public static final String SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON = "android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
+ field public static final String SYSTEM_APPLICATION_OVERLAY = "android.permission.SYSTEM_APPLICATION_OVERLAY";
field public static final String SYSTEM_CAMERA = "android.permission.SYSTEM_CAMERA";
field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
field public static final String TOGGLE_AUTOMOTIVE_PROJECTION = "android.permission.TOGGLE_AUTOMOTIVE_PROJECTION";
@@ -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";
@@ -2739,6 +2743,8 @@
}
public final class HdmiControlManager {
+ method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHdmiCecEnabledChangeListener(@NonNull android.hardware.hdmi.HdmiControlManager.CecSettingChangeListener);
+ method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHdmiCecEnabledChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.hdmi.HdmiControlManager.CecSettingChangeListener);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHotplugEventListener(android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHotplugEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public java.util.List<java.lang.Integer> getAllowedCecSettingIntValues(@NonNull String);
@@ -2757,6 +2763,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public java.util.List<java.lang.String> getUserCecSettings();
method public boolean isDeviceConnected(@NonNull android.hardware.hdmi.HdmiDeviceInfo);
method public void powerOffDevice(@NonNull android.hardware.hdmi.HdmiDeviceInfo);
+ method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void removeHdmiCecEnabledChangeListener(@NonNull android.hardware.hdmi.HdmiControlManager.CecSettingChangeListener);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void removeHotplugEventListener(android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
method public void setActiveSource(@NonNull android.hardware.hdmi.HdmiDeviceInfo);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setHdmiCecEnabled(@NonNull int);
@@ -2869,6 +2876,10 @@
field public static final int TIMER_STATUS_PROGRAMMED_INFO_NO_MEDIA_INFO = 10; // 0xa
}
+ public static interface HdmiControlManager.CecSettingChangeListener {
+ method public void onChange(@NonNull String);
+ }
+
@IntDef({android.hardware.hdmi.HdmiControlManager.RESULT_SUCCESS, android.hardware.hdmi.HdmiControlManager.RESULT_TIMEOUT, android.hardware.hdmi.HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE, android.hardware.hdmi.HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE, android.hardware.hdmi.HdmiControlManager.RESULT_ALREADY_IN_PROGRESS, android.hardware.hdmi.HdmiControlManager.RESULT_EXCEPTION, android.hardware.hdmi.HdmiControlManager.RESULT_INCORRECT_MODE, android.hardware.hdmi.HdmiControlManager.RESULT_COMMUNICATION_FAILED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public static @interface HdmiControlManager.ControlCallbackResult {
}
@@ -3977,6 +3988,25 @@
method public void onLocationBatch(java.util.List<android.location.Location>);
}
+ public final class CorrelationVector implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0) public int getFrequencyOffsetMetersPerSecond();
+ method @NonNull public int[] getMagnitude();
+ method @FloatRange(from=0.0f) public double getSamplingStartMeters();
+ method @FloatRange(from=0.0f, fromInclusive=false) public double getSamplingWidthMeters();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.CorrelationVector> CREATOR;
+ }
+
+ public static final class CorrelationVector.Builder {
+ ctor public CorrelationVector.Builder();
+ method @NonNull public android.location.CorrelationVector build();
+ method @NonNull public android.location.CorrelationVector.Builder setFrequencyOffsetMetersPerSecond(@IntRange(from=0) int);
+ method @NonNull public android.location.CorrelationVector.Builder setMagnitude(@NonNull int[]);
+ method @NonNull public android.location.CorrelationVector.Builder setSamplingStartMeters(@FloatRange(from=0.0f) double);
+ method @NonNull public android.location.CorrelationVector.Builder setSamplingWidthMeters(@FloatRange(from=0.0f, fromInclusive=false) double);
+ }
+
public final class GnssCapabilities implements android.os.Parcelable {
method public boolean hasGeofencing();
method public boolean hasLowPowerMode();
@@ -3985,6 +4015,7 @@
method public boolean hasMeasurementCorrectionsLosSats();
method @Deprecated public boolean hasMeasurementCorrectionsReflectingPane();
method public boolean hasMeasurementCorrectionsReflectingPlane();
+ method public boolean hasMeasurementCorrelationVectors();
method @Deprecated public boolean hasNavMessages();
method @Deprecated public boolean hasSatelliteBlacklist();
method public boolean hasSatelliteBlocklist();
@@ -3998,12 +4029,15 @@
method @NonNull public android.location.GnssCapabilities.Builder setHasMeasurementCorrectionsExcessPathLength(boolean);
method @NonNull public android.location.GnssCapabilities.Builder setHasMeasurementCorrectionsLosSats(boolean);
method @NonNull public android.location.GnssCapabilities.Builder setHasMeasurementCorrectionsReflectingPlane(boolean);
+ method @NonNull public android.location.GnssCapabilities.Builder setHasMeasurementCorrelationVectors(boolean);
method @NonNull public android.location.GnssCapabilities.Builder setHasSatelliteBlocklist(boolean);
method @NonNull public android.location.GnssCapabilities.Builder setHasSatellitePvt(boolean);
}
public final class GnssMeasurement implements android.os.Parcelable {
+ method @Nullable public java.util.Collection<android.location.CorrelationVector> getCorrelationVectors();
method @Nullable public android.location.SatellitePvt getSatellitePvt();
+ method public boolean hasCorrelationVectors();
method public boolean hasSatellitePvt();
}
@@ -4037,6 +4071,14 @@
method @NonNull public android.location.GnssMeasurementCorrections.Builder setVerticalPositionUncertaintyMeters(@FloatRange(from=0.0f) double);
}
+ public final class GnssMeasurementRequest implements android.os.Parcelable {
+ method public boolean isCorrelationVectorOutputsEnabled();
+ }
+
+ public static final class GnssMeasurementRequest.Builder {
+ method @NonNull public android.location.GnssMeasurementRequest.Builder setCorrelationVectorOutputsEnabled(boolean);
+ }
+
public final class GnssReflectingPlane implements android.os.Parcelable {
method public int describeContents();
method @FloatRange(from=-1000.0F, to=10000.0f) public double getAltitudeMeters();
@@ -5313,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);
@@ -7815,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();
@@ -10205,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);
@@ -10381,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);
}
@@ -10564,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 {
@@ -13334,9 +13365,10 @@
method public final void setUserActivityTimeout(long);
field @RequiresPermission(android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS) public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000
field @RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW) public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 16; // 0x10
+ field @RequiresPermission(android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY) public static final int SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY = 8; // 0x8
}
- @IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags {
+ @IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS, android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags {
}
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 62c660c..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>);
}
}
@@ -730,6 +746,18 @@
}
+package android.hardware {
+
+ public final class SensorPrivacyManager {
+ method public boolean isIndividualSensorPrivacyEnabled(int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setIndividualSensorPrivacy(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setIndividualSensorPrivacyForProfileGroup(int, boolean);
+ field public static final int INDIVIDUAL_SENSOR_CAMERA = 2; // 0x2
+ field public static final int INDIVIDUAL_SENSOR_MICROPHONE = 1; // 0x1
+ }
+
+}
+
package android.hardware.biometrics {
public class BiometricManager {
@@ -929,6 +957,7 @@
method @Deprecated public void resetCarrierPhase();
method @Deprecated public void resetCarrierPhaseUncertainty();
method public void resetCodeType();
+ method public void resetCorrelationVectors();
method public void resetFullInterSignalBiasNanos();
method public void resetFullInterSignalBiasUncertaintyNanos();
method public void resetSatelliteInterSignalBiasNanos();
@@ -948,6 +977,7 @@
method public void setCn0DbHz(double);
method public void setCodeType(@NonNull String);
method public void setConstellationType(int);
+ method public void setCorrelationVectors(@Nullable java.util.Collection<android.location.CorrelationVector>);
method public void setFullInterSignalBiasNanos(double);
method public void setFullInterSignalBiasUncertaintyNanos(@FloatRange(from=0.0) double);
method public void setMultipathIndicator(int);
@@ -1337,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;
@@ -1595,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();
@@ -2362,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/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 3bd88a4..216d340 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -468,6 +468,8 @@
GetterSetterNames: android.location.GnssMeasurement#setCarrierFrequencyHz(float):
GetterSetterNames: android.location.GnssMeasurement#setCodeType(String):
+
+GetterSetterNames: android.location.GnssMeasurement#setCorrelationVectors(java.util.Collection<android.location.CorrelationVector>):
GetterSetterNames: android.location.GnssMeasurement#setFullInterSignalBiasNanos(double):
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 b1890b0..986051c 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -98,11 +98,15 @@
public abstract void killForegroundAppsForUser(@UserIdInt int userId);
/**
- * Sets how long a {@link PendingIntent} can be temporarily whitelist to by bypass restrictions
- * such as Power Save mode.
+ * Sets how long a {@link PendingIntent} can be temporarily allowlisted to bypass restrictions
+ * such as Power Save mode.
+ * @param target
+ * @param whitelistToken
+ * @param duration temp allowlist duration in milliseconds.
+ * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}
*/
public abstract void setPendingIntentWhitelistDuration(IIntentSender target,
- IBinder whitelistToken, long duration);
+ IBinder whitelistToken, long duration, int type);
/**
* Returns the flags set for a {@link PendingIntent}.
@@ -128,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
@@ -318,8 +327,17 @@
public abstract boolean isBooted();
public abstract void finishBooting();
+ /**
+ * Temp allowlist a UID for PendingIntent.
+ * @param callerPid the PID that sent the PendingIntent.
+ * @param callerUid the UID that sent the PendingIntent.
+ * @param targetUid the UID that is been temp allowlisted.
+ * @param duration temp allowlist duration in milliseconds.
+ * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+ * @param tag
+ */
public abstract void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid,
- long duration, String tag);
+ long duration, int type, String tag);
public abstract int broadcastIntentInPackage(String packageName, @Nullable String featureId,
int uid, int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
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 e62cd4f..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;
@@ -1329,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 ecb96e1..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
@@ -12870,6 +12982,7 @@
* @throws SecurityException if the caller is not a profile owner or device owner.
*/
@NonNull public String getEnrollmentSpecificId() {
+ throwIfParentInstance("getEnrollmentSpecificId");
if (mService == null) {
return "";
}
@@ -12892,6 +13005,7 @@
* enrolled into.
*/
public void setOrganizationId(@NonNull String enterpriseId) {
+ throwIfParentInstance("setOrganizationId");
setOrganizationIdForUser(mContext.getPackageName(), enterpriseId, myUserId());
}
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index ce2fd4f..a0d2977 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -231,7 +231,13 @@
* Returns the profile owner component for the given user, or {@code null} if there is not one.
*/
@Nullable
- public abstract ComponentName getProfileOwnerAsUser(int userHandle);
+ public abstract ComponentName getProfileOwnerAsUser(@UserIdInt int userId);
+
+ /**
+ * Returns the user id of the device owner, or {@link UserHandle#USER_NULL} if there is not one.
+ */
+ @UserIdInt
+ public abstract int getDeviceOwnerUserId();
/**
* Returns whether the given package is a device owner or a profile owner in the calling user.
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/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index ee718b35..b216e91 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -55,12 +55,6 @@
String SHELL_COMMAND_IS_GEO_DETECTION_SUPPORTED = "is_geo_detection_supported";
/**
- * A shell command that prints the current user's "location enabled" setting.
- * @hide
- */
- String SHELL_COMMAND_IS_LOCATION_ENABLED = "is_location_enabled";
-
- /**
* A shell command that prints the current user's "location-based time zone detection enabled"
* setting.
* @hide
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 5736985..2190140 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4842,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.
@@ -5105,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/IntentFilter.java b/core/java/android/content/IntentFilter.java
index f063359..c5badb9 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -150,6 +150,7 @@
private static final String AGLOB_STR = "aglob";
private static final String SGLOB_STR = "sglob";
private static final String PREFIX_STR = "prefix";
+ private static final String SUFFIX_STR = "suffix";
private static final String LITERAL_STR = "literal";
private static final String PATH_STR = "path";
private static final String PORT_STR = "port";
@@ -1226,7 +1227,8 @@
* path, or a simple pattern, depending on <var>type</var>.
* @param type Determines how <var>ssp</var> will be compared to
* determine a match: either {@link PatternMatcher#PATTERN_LITERAL},
- * {@link PatternMatcher#PATTERN_PREFIX}, or
+ * {@link PatternMatcher#PATTERN_PREFIX},
+ * {@link PatternMatcher#PATTERN_SUFFIX}, or
* {@link PatternMatcher#PATTERN_SIMPLE_GLOB}.
*
* @see #matchData
@@ -1419,7 +1421,8 @@
* path, or a simple pattern, depending on <var>type</var>.
* @param type Determines how <var>path</var> will be compared to
* determine a match: either {@link PatternMatcher#PATTERN_LITERAL},
- * {@link PatternMatcher#PATTERN_PREFIX}, or
+ * {@link PatternMatcher#PATTERN_PREFIX},
+ * {@link PatternMatcher#PATTERN_SUFFIX}, or
* {@link PatternMatcher#PATTERN_SIMPLE_GLOB}.
*
* @see #matchData
@@ -1920,6 +1923,9 @@
case PatternMatcher.PATTERN_ADVANCED_GLOB:
serializer.attribute(null, AGLOB_STR, pe.getPath());
break;
+ case PatternMatcher.PATTERN_SUFFIX:
+ serializer.attribute(null, SUFFIX_STR, pe.getPath());
+ break;
}
serializer.endTag(null, SSP_STR);
}
@@ -1950,6 +1956,9 @@
case PatternMatcher.PATTERN_ADVANCED_GLOB:
serializer.attribute(null, AGLOB_STR, pe.getPath());
break;
+ case PatternMatcher.PATTERN_SUFFIX:
+ serializer.attribute(null, SUFFIX_STR, pe.getPath());
+ break;
}
serializer.endTag(null, PATH_STR);
}
@@ -2057,6 +2066,8 @@
addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_SIMPLE_GLOB);
} else if ((ssp=parser.getAttributeValue(null, AGLOB_STR)) != null) {
addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_ADVANCED_GLOB);
+ } else if ((ssp=parser.getAttributeValue(null, SUFFIX_STR)) != null) {
+ addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_SUFFIX);
}
} else if (tagName.equals(AUTH_STR)) {
String host = parser.getAttributeValue(null, HOST_STR);
@@ -2074,6 +2085,8 @@
addDataPath(path, PatternMatcher.PATTERN_SIMPLE_GLOB);
} else if ((path=parser.getAttributeValue(null, AGLOB_STR)) != null) {
addDataPath(path, PatternMatcher.PATTERN_ADVANCED_GLOB);
+ } else if ((path=parser.getAttributeValue(null, SUFFIX_STR)) != null) {
+ addDataPath(path, PatternMatcher.PATTERN_SUFFIX);
}
} else {
Log.w("IntentFilter", "Unknown tag parsing IntentFilter: " + tagName);
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/OWNERS b/core/java/android/content/pm/OWNERS
index fd32efc..f0def805 100644
--- a/core/java/android/content/pm/OWNERS
+++ b/core/java/android/content/pm/OWNERS
@@ -6,5 +6,6 @@
per-file PackageParser.java = chiuwinson@google.com
per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
+per-file AppSearchPerson.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS
per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS
per-file UserInfo* = file:/MULTIUSER_OWNERS
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 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/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
new file mode 100644
index 0000000..d8ec512
--- /dev/null
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -0,0 +1,412 @@
+/*
+ * 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.parsing;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageParser.SigningDetails;
+import android.content.pm.VerifierInfo;
+
+import com.android.internal.util.DataClass;
+
+import java.util.List;
+
+/**
+ * Lightweight parsed details about a single APK file.
+ *
+ * @hide
+ */
+@DataClass(genConstructor = false, genConstDefs = false)
+public class ApkLite {
+ /** Name of the package as used to identify it in the system */
+ private final @NonNull String mPackageName;
+ /** Path where this APK file was found on disk */
+ private final @NonNull String mPath;
+ /** Split name of this APK */
+ private final @Nullable String mSplitName;
+ /** Dependencies of the split APK */
+ /** Name of the split APK that this APK depends on */
+ private final @Nullable String mUsesSplitName;
+ /** Name of the split APK that this APK is a configuration for */
+ private final @Nullable String mConfigForSplit;
+
+ /** Major version number of this package */
+ private final int mVersionCodeMajor;
+ /** Minor version number of this package */
+ private final int mVersionCode;
+ /** Revision code of this APK */
+ private final int mRevisionCode;
+ /**
+ * Indicate the install location of this package
+ *
+ * @see {@link PackageInfo#INSTALL_LOCATION_AUTO}
+ * @see {@link PackageInfo#INSTALL_LOCATION_INTERNAL_ONLY}
+ * @see {@link PackageInfo#INSTALL_LOCATION_PREFER_EXTERNAL}
+ */
+ private final int mInstallLocation;
+ /** Indicate the minimum SDK version number that the app requires */
+ private final int mMinSdkVersion;
+ /** Indicate the SDK version number that the application is targeting */
+ private final int mTargetSdkVersion;
+ /** Information about a package verifiers as used during package verification */
+ private final @NonNull VerifierInfo[] mVerifiers;
+ /** Signing-related data of an application package */
+ private final @NonNull SigningDetails mSigningDetails;
+
+ /** Indicate whether this APK is a 'feature' split */
+ private final boolean mFeatureSplit;
+ /** Indicate whether each split should be load into their own Context objects */
+ private final boolean mIsolatedSplits;
+ /**
+ * Indicate whether this package requires at least one split (either feature or resource)
+ * to be present in order to function
+ */
+ private final boolean mSplitRequired;
+ /** Indicate whether this app is coreApp */
+ private final boolean mCoreApp;
+ /** Indicate whether this app can be debugged */
+ private final boolean mDebuggable;
+ /** Indicate whether this app is profileable by Shell */
+ private final boolean mProfileableByShell;
+ /** Indicate whether this app needs to be loaded into other applications' processes */
+ private final boolean mMultiArch;
+ /** Indicate whether the 32 bit version of the ABI should be used */
+ private final boolean mUse32bitAbi;
+ /** Indicate whether installer extracts native libraries */
+ private final boolean mExtractNativeLibs;
+ /**
+ * Indicate whether this package wants to run the dex within its APK but not extracted
+ * or locally compiled variants.
+ */
+ private final boolean mUseEmbeddedDex;
+
+ /** Name of the overlay-able set of elements package */
+ private final @Nullable String mTargetPackageName;
+ /** Indicate whether the overlay is static */
+ private final boolean mOverlayIsStatic;
+ /** Indicate the priority of this overlay package */
+ private final int mOverlayPriority;
+
+ /**
+ * Indicate the policy to deal with user data when rollback is committed
+ *
+ * @see {@link android.content.pm.PackageManager.RollbackDataPolicy#RESTORE}
+ * @see {@link android.content.pm.PackageManager.RollbackDataPolicy#WIPE}
+ * @see {@link android.content.pm.PackageManager.RollbackDataPolicy#RETAIN}
+ */
+ private final int mRollbackDataPolicy;
+
+ public ApkLite(String path, String packageName, String splitName, boolean isFeatureSplit,
+ String configForSplit, String usesSplitName, boolean isSplitRequired, int versionCode,
+ int versionCodeMajor, int revisionCode, int installLocation,
+ List<VerifierInfo> verifiers, SigningDetails signingDetails, boolean coreApp,
+ boolean debuggable, boolean profileableByShell, boolean multiArch, boolean use32bitAbi,
+ boolean useEmbeddedDex, boolean extractNativeLibs, boolean isolatedSplits,
+ String targetPackageName, boolean overlayIsStatic, int overlayPriority,
+ int minSdkVersion, int targetSdkVersion, int rollbackDataPolicy) {
+ mPath = path;
+ mPackageName = packageName;
+ mSplitName = splitName;
+ mFeatureSplit = isFeatureSplit;
+ mConfigForSplit = configForSplit;
+ mUsesSplitName = usesSplitName;
+ mSplitRequired = isSplitRequired;
+ mVersionCode = versionCode;
+ mVersionCodeMajor = versionCodeMajor;
+ mRevisionCode = revisionCode;
+ mInstallLocation = installLocation;
+ mVerifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
+ mSigningDetails = signingDetails;
+ mCoreApp = coreApp;
+ mDebuggable = debuggable;
+ mProfileableByShell = profileableByShell;
+ mMultiArch = multiArch;
+ mUse32bitAbi = use32bitAbi;
+ mUseEmbeddedDex = useEmbeddedDex;
+ mExtractNativeLibs = extractNativeLibs;
+ mIsolatedSplits = isolatedSplits;
+ mTargetPackageName = targetPackageName;
+ mOverlayIsStatic = overlayIsStatic;
+ mOverlayPriority = overlayPriority;
+ mMinSdkVersion = minSdkVersion;
+ mTargetSdkVersion = targetSdkVersion;
+ mRollbackDataPolicy = rollbackDataPolicy;
+ }
+
+ /**
+ * Return {@link #mVersionCode} and {@link #mVersionCodeMajor} combined together as a
+ * single long value. The {@link #mVersionCodeMajor} is placed in the upper 32 bits.
+ */
+ public long getLongVersionCode() {
+ return PackageInfo.composeLongVersionCode(mVersionCodeMajor, mVersionCode);
+ }
+
+
+
+ // 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/content/pm/parsing/ApkLite.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Name of the package as used to identify it in the system
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * Path where this APK file was found on disk
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getPath() {
+ return mPath;
+ }
+
+ /**
+ * Split name of this APK
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getSplitName() {
+ return mSplitName;
+ }
+
+ /**
+ * Name of the split APK that this APK depends on
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getUsesSplitName() {
+ return mUsesSplitName;
+ }
+
+ /**
+ * Name of the split APK that this APK is a configuration for
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getConfigForSplit() {
+ return mConfigForSplit;
+ }
+
+ /**
+ * Major version number of this package
+ */
+ @DataClass.Generated.Member
+ public int getVersionCodeMajor() {
+ return mVersionCodeMajor;
+ }
+
+ /**
+ * Minor version number of this package
+ */
+ @DataClass.Generated.Member
+ public int getVersionCode() {
+ return mVersionCode;
+ }
+
+ /**
+ * Revision code of this APK
+ */
+ @DataClass.Generated.Member
+ public int getRevisionCode() {
+ return mRevisionCode;
+ }
+
+ /**
+ * Indicate the install location of this package
+ *
+ * @see {@link PackageInfo#INSTALL_LOCATION_AUTO}
+ * @see {@link PackageInfo#INSTALL_LOCATION_INTERNAL_ONLY}
+ * @see {@link PackageInfo#INSTALL_LOCATION_PREFER_EXTERNAL}
+ */
+ @DataClass.Generated.Member
+ public int getInstallLocation() {
+ return mInstallLocation;
+ }
+
+ /**
+ * Indicate the minimum SDK version number that the app requires
+ */
+ @DataClass.Generated.Member
+ public int getMinSdkVersion() {
+ return mMinSdkVersion;
+ }
+
+ /**
+ * Indicate the SDK version number that the application is targeting
+ */
+ @DataClass.Generated.Member
+ public int getTargetSdkVersion() {
+ return mTargetSdkVersion;
+ }
+
+ /**
+ * Information about a package verifiers as used during package verification
+ */
+ @DataClass.Generated.Member
+ public @NonNull VerifierInfo[] getVerifiers() {
+ return mVerifiers;
+ }
+
+ /**
+ * Signing-related data of an application package
+ */
+ @DataClass.Generated.Member
+ public @NonNull SigningDetails getSigningDetails() {
+ return mSigningDetails;
+ }
+
+ /**
+ * Indicate whether this APK is a 'feature' split
+ */
+ @DataClass.Generated.Member
+ public boolean isFeatureSplit() {
+ return mFeatureSplit;
+ }
+
+ /**
+ * Indicate whether each split should be load into their own Context objects
+ */
+ @DataClass.Generated.Member
+ public boolean isIsolatedSplits() {
+ return mIsolatedSplits;
+ }
+
+ /**
+ * Indicate whether this package requires at least one split (either feature or resource)
+ * to be present in order to function
+ */
+ @DataClass.Generated.Member
+ public boolean isSplitRequired() {
+ return mSplitRequired;
+ }
+
+ /**
+ * Indicate whether this app is coreApp
+ */
+ @DataClass.Generated.Member
+ public boolean isCoreApp() {
+ return mCoreApp;
+ }
+
+ /**
+ * Indicate whether this app can be debugged
+ */
+ @DataClass.Generated.Member
+ public boolean isDebuggable() {
+ return mDebuggable;
+ }
+
+ /**
+ * Indicate whether this app is profileable by Shell
+ */
+ @DataClass.Generated.Member
+ public boolean isProfileableByShell() {
+ return mProfileableByShell;
+ }
+
+ /**
+ * Indicate whether this app needs to be loaded into other applications' processes
+ */
+ @DataClass.Generated.Member
+ public boolean isMultiArch() {
+ return mMultiArch;
+ }
+
+ /**
+ * Indicate whether the 32 bit version of the ABI should be used
+ */
+ @DataClass.Generated.Member
+ public boolean isUse32bitAbi() {
+ return mUse32bitAbi;
+ }
+
+ /**
+ * Indicate whether installer extracts native libraries
+ */
+ @DataClass.Generated.Member
+ public boolean isExtractNativeLibs() {
+ return mExtractNativeLibs;
+ }
+
+ /**
+ * Indicate whether this package wants to run the dex within its APK but not extracted
+ * or locally compiled variants.
+ */
+ @DataClass.Generated.Member
+ public boolean isUseEmbeddedDex() {
+ return mUseEmbeddedDex;
+ }
+
+ /**
+ * Name of the overlay-able set of elements package
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getTargetPackageName() {
+ return mTargetPackageName;
+ }
+
+ /**
+ * Indicate whether the overlay is static
+ */
+ @DataClass.Generated.Member
+ public boolean isOverlayIsStatic() {
+ return mOverlayIsStatic;
+ }
+
+ /**
+ * Indicate the priority of this overlay package
+ */
+ @DataClass.Generated.Member
+ public int getOverlayPriority() {
+ return mOverlayPriority;
+ }
+
+ /**
+ * Indicate the policy to deal with user data when rollback is committed
+ *
+ * @see {@link android.content.pm.PackageManager.RollbackDataPolicy#RESTORE}
+ * @see {@link android.content.pm.PackageManager.RollbackDataPolicy#WIPE}
+ * @see {@link android.content.pm.PackageManager.RollbackDataPolicy#RETAIN}
+ */
+ @DataClass.Generated.Member
+ public int getRollbackDataPolicy() {
+ return mRollbackDataPolicy;
+ }
+
+ @DataClass.Generated(
+ time = 1610596637723L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.PackageParser.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final int mRollbackDataPolicy\npublic long getLongVersionCode()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index f583e25..51b81b6 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -18,7 +18,6 @@
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
-import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
import static android.content.pm.parsing.ParsingPackageUtils.validateName;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
@@ -51,6 +50,7 @@
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Comparator;
import java.util.List;
import java.util.Objects;
@@ -66,6 +66,8 @@
private static final int PARSE_DEFAULT_INSTALL_LOCATION =
PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+ public static final String APK_FILE_EXTENSION = ".apk";
+
/**
* Parse only lightweight details about the package at the given location.
* Automatically detects if the package is a monolithic style (single APK
@@ -606,4 +608,21 @@
return new VerifierInfo(packageName, publicKey);
}
+
+ /**
+ * Used to sort a set of APKs based on their split names, always placing the
+ * base APK (with {@code null} split name) first.
+ */
+ private static class SplitNameComparator implements Comparator<String> {
+ @Override
+ public int compare(String lhs, String rhs) {
+ if (lhs == null) {
+ return -1;
+ } else if (rhs == null) {
+ return 1;
+ } else {
+ return lhs.compareTo(rhs);
+ }
+ }
+ }
}
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/content/pm/parsing/PackageLite.java b/core/java/android/content/pm/parsing/PackageLite.java
new file mode 100644
index 0000000..803d643
--- /dev/null
+++ b/core/java/android/content/pm/parsing/PackageLite.java
@@ -0,0 +1,363 @@
+/*
+ * 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.parsing;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageInfo;
+import android.content.pm.VerifierInfo;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.DataClass;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Lightweight parsed details about a single package.
+ *
+ * @hide
+ */
+@DataClass(genConstructor = false, genConstDefs = false)
+public class PackageLite {
+ /** Name of the package as used to identify it in the system */
+ private final @NonNull String mPackageName;
+ /**
+ * Path where this package was found on disk. For monolithic packages
+ * this is path to single base APK file; for cluster packages this is
+ * path to the cluster directory.
+ */
+ private final @NonNull String mPath;
+ /** Path of base APK */
+ private final @NonNull String mBaseApkPath;
+ /** Paths of any split APKs, ordered by parsed splitName */
+ private final @Nullable String[] mSplitApkPaths;
+ /** Names of any split APKs, ordered by parsed splitName */
+ private final @Nullable String[] mSplitNames;
+ /** Dependencies of any split APKs, ordered by parsed splitName */
+ private final @Nullable String[] mUsesSplitNames;
+ private final @Nullable String[] mConfigForSplit;
+ /** Major and minor version number of this package */
+ private final int mVersionCodeMajor;
+ private final int mVersionCode;
+ /** Revision code of base APK */
+ private final int mBaseRevisionCode;
+ /** Revision codes of any split APKs, ordered by parsed splitName */
+ private final @Nullable int[] mSplitRevisionCodes;
+ /**
+ * Indicate the install location of this package
+ *
+ * @see {@link PackageInfo#INSTALL_LOCATION_AUTO}
+ * @see {@link PackageInfo#INSTALL_LOCATION_INTERNAL_ONLY}
+ * @see {@link PackageInfo#INSTALL_LOCATION_PREFER_EXTERNAL}
+ */
+ private final int mInstallLocation;
+ /** Information about a package verifiers as used during package verification */
+ private final @NonNull VerifierInfo[] mVerifiers;
+
+ /** Indicate whether any split APKs that are features. Ordered by splitName */
+ private final @Nullable boolean[] mIsFeatureSplits;
+ /** Indicate whether each split should be load into their own Context objects */
+ private final boolean mIsolatedSplits;
+ /**
+ * Indicate whether this package requires at least one split (either feature or resource)
+ * to be present in order to function
+ */
+ private final boolean mSplitRequired;
+ /** Indicate whether this app is coreApp */
+ private final boolean mCoreApp;
+ /** Indicate whether this app can be debugged */
+ private final boolean mDebuggable;
+ /** Indicate whether this app needs to be loaded into other applications' processes */
+ private final boolean mMultiArch;
+ /** Indicate whether the 32 bit version of the ABI should be used */
+ private final boolean mUse32bitAbi;
+ /** Indicate whether installer extracts native libraries */
+ private final boolean mExtractNativeLibs;
+ /** Indicate whether this app is profileable by Shell */
+ private final boolean mProfileableByShell;
+ /**
+ * Indicate whether this package wants to run the dex within its APK but not extracted
+ * or locally compiled variants.
+ */
+ private final boolean mUseEmbeddedDex;
+
+ public PackageLite(String path, String baseApkPath, ApkLite baseApk,
+ String[] splitNames, boolean[] isFeatureSplits, String[] usesSplitNames,
+ String[] configForSplit, String[] splitApkPaths, int[] splitRevisionCodes) {
+ // The following paths may be different from the path in ApkLite because we
+ // move or rename the APK files. Use parameters to indicate the correct paths.
+ mPath = path;
+ mBaseApkPath = baseApkPath;
+ mPackageName = baseApk.getPackageName();
+ mVersionCode = baseApk.getVersionCode();
+ mVersionCodeMajor = baseApk.getVersionCodeMajor();
+ mInstallLocation = baseApk.getInstallLocation();
+ mVerifiers = baseApk.getVerifiers();
+ mBaseRevisionCode = baseApk.getRevisionCode();
+ mCoreApp = baseApk.isCoreApp();
+ mDebuggable = baseApk.isDebuggable();
+ mMultiArch = baseApk.isMultiArch();
+ mUse32bitAbi = baseApk.isUse32bitAbi();
+ mExtractNativeLibs = baseApk.isExtractNativeLibs();
+ mIsolatedSplits = baseApk.isIsolatedSplits();
+ mUseEmbeddedDex = baseApk.isUseEmbeddedDex();
+ mSplitRequired = baseApk.isSplitRequired();
+ mProfileableByShell = baseApk.isProfileableByShell();
+ mSplitNames = splitNames;
+ mIsFeatureSplits = isFeatureSplits;
+ mUsesSplitNames = usesSplitNames;
+ mConfigForSplit = configForSplit;
+ mSplitApkPaths = splitApkPaths;
+ mSplitRevisionCodes = splitRevisionCodes;
+ }
+
+ /**
+ * Return code path to the base APK file, and split APK files if any.
+ */
+ public List<String> getAllApkPaths() {
+ final ArrayList<String> paths = new ArrayList<>();
+ paths.add(mBaseApkPath);
+ if (!ArrayUtils.isEmpty(mSplitApkPaths)) {
+ Collections.addAll(paths, mSplitApkPaths);
+ }
+ return paths;
+ }
+
+ /**
+ * Return {@link #mVersionCode} and {@link #mVersionCodeMajor} combined together as a
+ * single long value. The {@link #mVersionCodeMajor} is placed in the upper 32 bits.
+ */
+ public long getLongVersionCode() {
+ return PackageInfo.composeLongVersionCode(mVersionCodeMajor, mVersionCode);
+ }
+
+
+
+ // 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/content/pm/parsing/PackageLite.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Name of the package as used to identify it in the system
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * Path where this package was found on disk. For monolithic packages
+ * this is path to single base APK file; for cluster packages this is
+ * path to the cluster directory.
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getPath() {
+ return mPath;
+ }
+
+ /**
+ * Path of base APK
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getBaseApkPath() {
+ return mBaseApkPath;
+ }
+
+ /**
+ * Paths of any split APKs, ordered by parsed splitName
+ */
+ @DataClass.Generated.Member
+ public @Nullable String[] getSplitApkPaths() {
+ return mSplitApkPaths;
+ }
+
+ /**
+ * Names of any split APKs, ordered by parsed splitName
+ */
+ @DataClass.Generated.Member
+ public @Nullable String[] getSplitNames() {
+ return mSplitNames;
+ }
+
+ /**
+ * Dependencies of any split APKs, ordered by parsed splitName
+ */
+ @DataClass.Generated.Member
+ public @Nullable String[] getUsesSplitNames() {
+ return mUsesSplitNames;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable String[] getConfigForSplit() {
+ return mConfigForSplit;
+ }
+
+ /**
+ * Major and minor version number of this package
+ */
+ @DataClass.Generated.Member
+ public int getVersionCodeMajor() {
+ return mVersionCodeMajor;
+ }
+
+ @DataClass.Generated.Member
+ public int getVersionCode() {
+ return mVersionCode;
+ }
+
+ /**
+ * Revision code of base APK
+ */
+ @DataClass.Generated.Member
+ public int getBaseRevisionCode() {
+ return mBaseRevisionCode;
+ }
+
+ /**
+ * Revision codes of any split APKs, ordered by parsed splitName
+ */
+ @DataClass.Generated.Member
+ public @Nullable int[] getSplitRevisionCodes() {
+ return mSplitRevisionCodes;
+ }
+
+ /**
+ * Indicate the install location of this package
+ *
+ * @see {@link PackageInfo#INSTALL_LOCATION_AUTO}
+ * @see {@link PackageInfo#INSTALL_LOCATION_INTERNAL_ONLY}
+ * @see {@link PackageInfo#INSTALL_LOCATION_PREFER_EXTERNAL}
+ */
+ @DataClass.Generated.Member
+ public int getInstallLocation() {
+ return mInstallLocation;
+ }
+
+ /**
+ * Information about a package verifiers as used during package verification
+ */
+ @DataClass.Generated.Member
+ public @NonNull VerifierInfo[] getVerifiers() {
+ return mVerifiers;
+ }
+
+ /**
+ * Indicate whether any split APKs that are features. Ordered by splitName
+ */
+ @DataClass.Generated.Member
+ public @Nullable boolean[] getIsFeatureSplits() {
+ return mIsFeatureSplits;
+ }
+
+ /**
+ * Indicate whether each split should be load into their own Context objects
+ */
+ @DataClass.Generated.Member
+ public boolean isIsolatedSplits() {
+ return mIsolatedSplits;
+ }
+
+ /**
+ * Indicate whether this package requires at least one split (either feature or resource)
+ * to be present in order to function
+ */
+ @DataClass.Generated.Member
+ public boolean isSplitRequired() {
+ return mSplitRequired;
+ }
+
+ /**
+ * Indicate whether this app is coreApp
+ */
+ @DataClass.Generated.Member
+ public boolean isCoreApp() {
+ return mCoreApp;
+ }
+
+ /**
+ * Indicate whether this app can be debugged
+ */
+ @DataClass.Generated.Member
+ public boolean isDebuggable() {
+ return mDebuggable;
+ }
+
+ /**
+ * Indicate whether this app needs to be loaded into other applications' processes
+ */
+ @DataClass.Generated.Member
+ public boolean isMultiArch() {
+ return mMultiArch;
+ }
+
+ /**
+ * Indicate whether the 32 bit version of the ABI should be used
+ */
+ @DataClass.Generated.Member
+ public boolean isUse32bitAbi() {
+ return mUse32bitAbi;
+ }
+
+ /**
+ * Indicate whether installer extracts native libraries
+ */
+ @DataClass.Generated.Member
+ public boolean isExtractNativeLibs() {
+ return mExtractNativeLibs;
+ }
+
+ /**
+ * Indicate whether this app is profileable by Shell
+ */
+ @DataClass.Generated.Member
+ public boolean isProfileableByShell() {
+ return mProfileableByShell;
+ }
+
+ /**
+ * Indicate whether this package wants to run the dex within its APK but not extracted
+ * or locally compiled variants.
+ */
+ @DataClass.Generated.Member
+ public boolean isUseEmbeddedDex() {
+ return mUseEmbeddedDex;
+ }
+
+ @DataClass.Generated(
+ time = 1610596639255L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/content/pm/parsing/PackageLite.java",
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 561a9e3..494b3cc 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -29,6 +29,7 @@
import android.annotation.AnyRes;
import android.annotation.CheckResult;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -112,6 +113,8 @@
import java.io.File;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Arrays;
@@ -130,6 +133,82 @@
private static final String TAG = ParsingUtils.TAG;
+ public static final boolean DEBUG_JAR = false;
+ public static final boolean DEBUG_BACKUP = false;
+ public static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
+
+ /** File name in an APK for the Android manifest. */
+ public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
+
+ /** Path prefix for apps on expanded storage */
+ public static final String MNT_EXPAND = "/mnt/expand/";
+
+ public static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
+ public static final String TAG_APPLICATION = "application";
+ public static final String TAG_COMPATIBLE_SCREENS = "compatible-screens";
+ public static final String TAG_EAT_COMMENT = "eat-comment";
+ public static final String TAG_FEATURE_GROUP = "feature-group";
+ public static final String TAG_INSTRUMENTATION = "instrumentation";
+ public static final String TAG_KEY_SETS = "key-sets";
+ public static final String TAG_MANIFEST = "manifest";
+ public static final String TAG_ORIGINAL_PACKAGE = "original-package";
+ public static final String TAG_OVERLAY = "overlay";
+ public static final String TAG_PACKAGE = "package";
+ public static final String TAG_PACKAGE_VERIFIER = "package-verifier";
+ public static final String TAG_ATTRIBUTION = "attribution";
+ public static final String TAG_PERMISSION = "permission";
+ public static final String TAG_PERMISSION_GROUP = "permission-group";
+ public static final String TAG_PERMISSION_TREE = "permission-tree";
+ public static final String TAG_PROTECTED_BROADCAST = "protected-broadcast";
+ public static final String TAG_QUERIES = "queries";
+ public static final String TAG_RESTRICT_UPDATE = "restrict-update";
+ public static final String TAG_SUPPORT_SCREENS = "supports-screens";
+ public static final String TAG_SUPPORTS_INPUT = "supports-input";
+ public static final String TAG_USES_CONFIGURATION = "uses-configuration";
+ public static final String TAG_USES_FEATURE = "uses-feature";
+ public static final String TAG_USES_GL_TEXTURE = "uses-gl-texture";
+ public static final String TAG_USES_PERMISSION = "uses-permission";
+ public static final String TAG_USES_PERMISSION_SDK_23 = "uses-permission-sdk-23";
+ public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
+ public static final String TAG_USES_SDK = "uses-sdk";
+ public static final String TAG_USES_SPLIT = "uses-split";
+ public static final String TAG_PROFILEABLE = "profileable";
+
+ public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
+ public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
+ public static final String METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY =
+ "android.activity_window_layout_affinity";
+
+ public static final int SDK_VERSION = Build.VERSION.SDK_INT;
+ public static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
+
+ public static boolean sCompatibilityModeEnabled = true;
+ public static boolean sUseRoundIcon = false;
+
+ public static final int PARSE_DEFAULT_INSTALL_LOCATION =
+ PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+ public static final int PARSE_DEFAULT_TARGET_SANDBOX = 1;
+
+ public static final int PARSE_MUST_BE_APK = 1 << 0;
+ public static final int PARSE_IGNORE_PROCESSES = 1 << 1;
+ public static final int PARSE_EXTERNAL_STORAGE = 1 << 3;
+ public static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
+ public static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
+ public static final int PARSE_ENFORCE_CODE = 1 << 6;
+ public static final int PARSE_CHATTY = 1 << 31;
+
+ @IntDef(flag = true, prefix = { "PARSE_" }, value = {
+ PARSE_CHATTY,
+ PARSE_COLLECT_CERTIFICATES,
+ PARSE_ENFORCE_CODE,
+ PARSE_EXTERNAL_STORAGE,
+ PARSE_IGNORE_PROCESSES,
+ PARSE_IS_SYSTEM_DIR,
+ PARSE_MUST_BE_APK,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ParseFlags {}
+
/**
* For those names would be used as a part of the file name. Limits size to 223 and reserves 32
* for the OS.
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index 0d7198d..f96bd54 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.app.ActivityTaskManager;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageParser;
import android.content.pm.parsing.ParsingPackage;
@@ -33,6 +34,7 @@
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.os.Build;
+import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
@@ -50,12 +52,21 @@
import java.io.IOException;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/** @hide */
public class ParsedActivityUtils {
private static final String TAG = ParsingUtils.TAG;
+ public static final boolean LOG_UNSAFE_BROADCASTS = false;
+
+ // Set of broadcast actions that are safe for manifest receivers
+ public static final Set<String> SAFE_BROADCASTS = new ArraySet<>();
+ static {
+ SAFE_BROADCASTS.add(Intent.ACTION_BOOT_COMPLETED);
+ }
+
@NonNull
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public static ParseResult<ParsedActivity> parseActivityOrReceiver(String[] separateProcesses,
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
index c0536bb..368dcfd 100644
--- a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
@@ -44,6 +44,8 @@
private static final String TAG = ParsingUtils.TAG;
+ public static final boolean DEBUG = false;
+
@NonNull
public static ParseResult<ParsedIntentInfo> parseIntentInfo(String className,
ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean allowGlobs,
@@ -209,6 +211,25 @@
PatternMatcher.PATTERN_SIMPLE_GLOB);
}
+ str = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestData_sspAdvancedPattern, 0);
+ if (str != null) {
+ if (!allowGlobs) {
+ return input.error(
+ "sspAdvancedPattern not allowed here; ssp must be literal");
+ }
+ intentInfo.addDataSchemeSpecificPart(str,
+ PatternMatcher.PATTERN_ADVANCED_GLOB);
+ }
+
+ str = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestData_sspSuffix, 0);
+ if (str != null) {
+ intentInfo.addDataSchemeSpecificPart(str,
+ PatternMatcher.PATTERN_SUFFIX);
+ }
+
+
String host = sa.getNonConfigurationString(
R.styleable.AndroidManifestData_host, 0);
String port = sa.getNonConfigurationString(
@@ -249,6 +270,13 @@
intentInfo.addDataPath(str, PatternMatcher.PATTERN_ADVANCED_GLOB);
}
+ str = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestData_pathSuffix, 0);
+ if (str != null) {
+ intentInfo.addDataPath(str, PatternMatcher.PATTERN_SUFFIX);
+ }
+
+
return input.success(null);
} finally {
sa.recycle();
diff --git a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
index 90691f1..4deab56 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
@@ -211,22 +211,34 @@
R.styleable.AndroidManifestGrantUriPermission);
try {
String name = parser.getName();
- // Pattern has priority over prefix over literal path
+ // Pattern has priority over pre/suffix over literal path
PatternMatcher pa = null;
String str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestGrantUriPermission_pathPattern, 0);
+ R.styleable.AndroidManifestGrantUriPermission_pathAdvancedPattern, 0);
if (str != null) {
- pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
+ pa = new PatternMatcher(str, PatternMatcher.PATTERN_ADVANCED_GLOB);
} else {
str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestGrantUriPermission_pathPrefix, 0);
+ R.styleable.AndroidManifestGrantUriPermission_pathPattern, 0);
if (str != null) {
- pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX);
+ pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
} else {
str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestGrantUriPermission_path, 0);
+ R.styleable.AndroidManifestGrantUriPermission_pathPrefix, 0);
if (str != null) {
- pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL);
+ pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX);
+ } else {
+ str = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestGrantUriPermission_pathSuffix, 0);
+ if (str != null) {
+ pa = new PatternMatcher(str, PatternMatcher.PATTERN_SUFFIX);
+ } else {
+ str = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestGrantUriPermission_path, 0);
+ if (str != null) {
+ pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL);
+ }
+ }
}
}
}
@@ -318,10 +330,18 @@
pa = new PathPermission(path, PatternMatcher.PATTERN_PREFIX, readPermission,
writePermission);
} else {
- path = sa.getNonConfigurationString(R.styleable.AndroidManifestPathPermission_path, 0);
+ path = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestPathPermission_pathSuffix, 0);
if (path != null) {
- pa = new PathPermission(path, PatternMatcher.PATTERN_LITERAL,
+ pa = new PathPermission(path, PatternMatcher.PATTERN_SUFFIX,
readPermission, writePermission);
+ } else {
+ path = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestPathPermission_path, 0);
+ if (path != null) {
+ pa = new PathPermission(path, PatternMatcher.PATTERN_LITERAL,
+ readPermission, writePermission);
+ }
}
}
}
diff --git a/core/java/android/hardware/CameraStreamStats.java b/core/java/android/hardware/CameraStreamStats.java
index ae801b6..41d1e25 100644
--- a/core/java/android/hardware/CameraStreamStats.java
+++ b/core/java/android/hardware/CameraStreamStats.java
@@ -19,7 +19,6 @@
import android.os.Parcelable;
import android.util.Log;
-import java.util.ArrayList;
/**
* The camera stream statistics used for passing camera stream information from
* camera service to camera service proxy.
@@ -30,6 +29,8 @@
* @hide
*/
public class CameraStreamStats implements Parcelable {
+ public static final int HISTOGRAM_TYPE_UNKNOWN = 0;
+ public static final int HISTOGRAM_TYPE_CAPTURE_LATENCY = 1;
private int mWidth;
private int mHeight;
@@ -41,6 +42,9 @@
private int mStartLatencyMs;
private int mMaxHalBuffers;
private int mMaxAppBuffers;
+ private int mHistogramType;
+ private float[] mHistogramBins;
+ private long[] mHistogramCounts;
private static final String TAG = "CameraStreamStats";
@@ -55,6 +59,7 @@
mStartLatencyMs = 0;
mMaxHalBuffers = 0;
mMaxAppBuffers = 0;
+ mHistogramType = HISTOGRAM_TYPE_UNKNOWN;
}
public CameraStreamStats(int width, int height, int format,
@@ -70,6 +75,7 @@
mStartLatencyMs = startLatencyMs;
mMaxHalBuffers = maxHalBuffers;
mMaxAppBuffers = maxAppBuffers;
+ mHistogramType = HISTOGRAM_TYPE_UNKNOWN;
}
public static final @android.annotation.NonNull Parcelable.Creator<CameraStreamStats> CREATOR =
@@ -112,6 +118,9 @@
dest.writeInt(mStartLatencyMs);
dest.writeInt(mMaxHalBuffers);
dest.writeInt(mMaxAppBuffers);
+ dest.writeInt(mHistogramType);
+ dest.writeFloatArray(mHistogramBins);
+ dest.writeLongArray(mHistogramCounts);
}
public void readFromParcel(Parcel in) {
@@ -125,6 +134,9 @@
mStartLatencyMs = in.readInt();
mMaxHalBuffers = in.readInt();
mMaxAppBuffers = in.readInt();
+ mHistogramType = in.readInt();
+ mHistogramBins = in.createFloatArray();
+ mHistogramCounts = in.createLongArray();
}
public int getWidth() {
@@ -166,4 +178,16 @@
public int getMaxAppBuffers() {
return mMaxAppBuffers;
}
+
+ public int getHistogramType() {
+ return mHistogramType;
+ }
+
+ public float[] getHistogramBins() {
+ return mHistogramBins;
+ }
+
+ public long[] getHistogramCounts() {
+ return mHistogramCounts;
+ }
}
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 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/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index 8497525..e165ad6 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
@@ -40,14 +41,19 @@
*
* @hide
*/
+@TestApi
@SystemService(Context.SENSOR_PRIVACY_SERVICE)
public final class SensorPrivacyManager {
- /** Microphone */
+ /** Microphone
+ * @hide */
+ @TestApi
public static final int INDIVIDUAL_SENSOR_MICROPHONE =
SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
- /** Camera */
+ /** Camera
+ * @hide */
+ @TestApi
public static final int INDIVIDUAL_SENSOR_CAMERA =
SensorPrivacyIndividualEnabledSensorProto.CAMERA;
@@ -66,6 +72,8 @@
* A class implementing this interface can register with the {@link
* android.hardware.SensorPrivacyManager} to receive notification when the sensor privacy
* state changes.
+ *
+ * @hide
*/
public interface OnSensorPrivacyChangedListener {
/**
@@ -101,6 +109,8 @@
/**
* Returns the single instance of the SensorPrivacyManager.
+ *
+ * @hide
*/
public static SensorPrivacyManager getInstance(Context context) {
synchronized (sInstanceLock) {
@@ -121,6 +131,8 @@
* Sets sensor privacy to the specified state.
*
* @param enable the state to which sensor privacy should be set.
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setSensorPrivacy(boolean enable) {
@@ -137,6 +149,8 @@
*
* @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
* privacy changes.
+ *
+ * @hide
*/
public void addSensorPrivacyListener(final OnSensorPrivacyChangedListener listener) {
synchronized (mListeners) {
@@ -166,6 +180,8 @@
* @param sensor the sensor to listen to changes to
* @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
* privacy changes.
+ *
+ * @hide
*/
public void addSensorPrivacyListener(@IndividualSensor int sensor,
final OnSensorPrivacyChangedListener listener) {
@@ -196,6 +212,8 @@
*
* @param listener the OnSensorPrivacyChangedListener to be unregistered from notifications when
* sensor privacy changes.
+ *
+ * @hide
*/
public void removeSensorPrivacyListener(OnSensorPrivacyChangedListener listener) {
synchronized (mListeners) {
@@ -215,6 +233,8 @@
* Returns whether sensor privacy is currently enabled.
*
* @return true if sensor privacy is currently enabled, false otherwise.
+ *
+ * @hide
*/
public boolean isSensorPrivacyEnabled() {
try {
@@ -228,7 +248,10 @@
* Returns whether sensor privacy is currently enabled for a specific sensor.
*
* @return true if sensor privacy is currently enabled, false otherwise.
+ *
+ * @hide
*/
+ @TestApi
public boolean isIndividualSensorPrivacyEnabled(@IndividualSensor int sensor) {
try {
return mService.isIndividualSensorPrivacyEnabled(mContext.getUserId(), sensor);
@@ -242,7 +265,10 @@
*
* @param sensor the sensor which to change the state for
* @param enable the state to which sensor privacy should be set.
+ *
+ * @hide
*/
+ @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setIndividualSensorPrivacy(@IndividualSensor int sensor,
boolean enable) {
@@ -259,7 +285,10 @@
*
* @param sensor the sensor which to change the state for
* @param enable the state to which sensor privacy should be set.
+ *
+ * @hide
*/
+ @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setIndividualSensorPrivacyForProfileGroup(@IndividualSensor int sensor,
boolean enable) {
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 38ffc80..b09eda4 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -1372,6 +1372,87 @@
}
/**
+ * Listener used to get setting change notification.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface CecSettingChangeListener {
+ /**
+ * Called when value of a setting changes.
+ *
+ * @param setting name of a CEC setting that changed
+ */
+ void onChange(@NonNull @CecSettingName String setting);
+ }
+
+ private final ArrayMap<String,
+ ArrayMap<CecSettingChangeListener, IHdmiCecSettingChangeListener>>
+ mCecSettingChangeListeners = new ArrayMap<>();
+
+ private void addCecSettingChangeListener(
+ @NonNull @CecSettingName String setting,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull CecSettingChangeListener listener) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ return;
+ }
+ if (mCecSettingChangeListeners.containsKey(setting)
+ && mCecSettingChangeListeners.get(setting).containsKey(listener)) {
+ Log.e(TAG, "listener is already registered");
+ return;
+ }
+ IHdmiCecSettingChangeListener wrappedListener =
+ getCecSettingChangeListenerWrapper(executor, listener);
+ if (!mCecSettingChangeListeners.containsKey(setting)) {
+ mCecSettingChangeListeners.put(setting, new ArrayMap<>());
+ }
+ mCecSettingChangeListeners.get(setting).put(listener, wrappedListener);
+ try {
+ mService.addCecSettingChangeListener(setting, wrappedListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private void removeCecSettingChangeListener(
+ @NonNull @CecSettingName String setting,
+ @NonNull CecSettingChangeListener listener) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ return;
+ }
+ IHdmiCecSettingChangeListener wrappedListener =
+ !mCecSettingChangeListeners.containsKey(setting) ? null :
+ mCecSettingChangeListeners.get(setting).remove(listener);
+ if (wrappedListener == null) {
+ Log.e(TAG, "tried to remove not-registered listener");
+ return;
+ }
+ try {
+ mService.removeCecSettingChangeListener(setting, wrappedListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private IHdmiCecSettingChangeListener getCecSettingChangeListenerWrapper(
+ Executor executor, final CecSettingChangeListener listener) {
+ return new IHdmiCecSettingChangeListener.Stub() {
+ @Override
+ public void onChange(String setting) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> listener.onChange(setting));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+ }
+
+ /**
* Get a set of user-modifiable settings.
*
* @return a set of user-modifiable settings.
@@ -1493,6 +1574,53 @@
}
/**
+ * Add change listener for global status of HDMI CEC.
+ *
+ * <p>To stop getting the notification,
+ * use {@link #removeHdmiCecEnabledChangeListener(CecSettingChangeListener)}.
+ *
+ * Note that each invocation of the callback will be executed on an arbitrary
+ * Binder thread. This means that all callback implementations must be
+ * thread safe. To specify the execution thread, use
+ * {@link addHdmiCecEnabledChangeListener(Executor, CecSettingChangeListener)}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void addHdmiCecEnabledChangeListener(@NonNull CecSettingChangeListener listener) {
+ addHdmiCecEnabledChangeListener(ConcurrentUtils.DIRECT_EXECUTOR, listener);
+ }
+
+ /**
+ * Add change listener for global status of HDMI CEC.
+ *
+ * <p>To stop getting the notification,
+ * use {@link #removeHdmiCecEnabledChangeListener(CecSettingChangeListener)}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void addHdmiCecEnabledChangeListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull CecSettingChangeListener listener) {
+ addCecSettingChangeListener(CEC_SETTING_NAME_HDMI_CEC_ENABLED, executor, listener);
+ }
+
+ /**
+ * Remove change listener for global status of HDMI CEC.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void removeHdmiCecEnabledChangeListener(
+ @NonNull CecSettingChangeListener listener) {
+ removeCecSettingChangeListener(CEC_SETTING_NAME_HDMI_CEC_ENABLED, listener);
+ }
+
+ /**
* Set the version of the HDMI CEC specification currently used.
*
* <p>Allows to select either CEC 1.4b or 2.0 to be used by the device.
diff --git a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
index 3b61911f..89a7afa8 100644
--- a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
+++ b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
@@ -306,6 +306,18 @@
}
@Override
+ public void addCecSettingChangeListener(String name,
+ IHdmiCecSettingChangeListener listener) {
+ HdmiControlServiceWrapper.this.addCecSettingChangeListener(name, listener);
+ }
+
+ @Override
+ public void removeCecSettingChangeListener(String name,
+ IHdmiCecSettingChangeListener listener) {
+ HdmiControlServiceWrapper.this.removeCecSettingChangeListener(name, listener);
+ }
+
+ @Override
public List<String> getUserCecSettings() {
return HdmiControlServiceWrapper.this.getUserCecSettings();
}
@@ -522,6 +534,14 @@
IHdmiCecVolumeControlFeatureListener listener) {}
/** @hide */
+ public void addCecSettingChangeListener(String name,
+ IHdmiCecSettingChangeListener listener) {}
+
+ /** @hide */
+ public void removeCecSettingChangeListener(String name,
+ IHdmiCecSettingChangeListener listener) {}
+
+ /** @hide */
public List<String> getUserCecSettings() {
return new ArrayList<>();
}
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl b/core/java/android/hardware/hdmi/IHdmiCecSettingChangeListener.aidl
similarity index 67%
rename from media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
rename to core/java/android/hardware/hdmi/IHdmiCecSettingChangeListener.aidl
index edf96dd..6f7a6f8 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiCecSettingChangeListener.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,14 @@
* limitations under the License.
*/
-package android.media.tv.tunerresourcemanager;
+package android.hardware.hdmi;
/**
- * Simple container of the FrontendInfo struct defined in the TunerHAL 1.0 interface.
+ * Callback interface definition for HDMI client to get informed of
+ * CEC setting change.
*
* @hide
*/
-parcelable TunerFrontendInfo {
- int handle;
-
- int frontendType;
-
- int exclusiveGroupId;
+oneway interface IHdmiCecSettingChangeListener {
+ void onChange(in String setting);
}
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 65bd856..d7329e0 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -18,6 +18,7 @@
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.IHdmiCecSettingChangeListener;
import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
@@ -89,6 +90,8 @@
boolean isHdmiCecVolumeControlEnabled();
void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute);
void setSystemAudioModeOnForAudioOnlySource();
+ void addCecSettingChangeListener(String name, IHdmiCecSettingChangeListener listener);
+ void removeCecSettingChangeListener(String name, IHdmiCecSettingChangeListener listener);
List<String> getUserCecSettings();
List<String> getAllowedCecSettingStringValues(String name);
int[] getAllowedCecSettingIntValues(String name);
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%
copy from media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
copy 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/core/java/android/net/vcn/VcnTransportInfo.java b/core/java/android/net/vcn/VcnTransportInfo.java
new file mode 100644
index 0000000..4d8cf91
--- /dev/null
+++ b/core/java/android/net/vcn/VcnTransportInfo.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.TransportInfo;
+import android.net.wifi.WifiInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.SubscriptionManager;
+
+import java.util.Objects;
+
+/**
+ * VcnTransportInfo contains information about the VCN's underlying transports for SysUi.
+ *
+ * <p>Presence of this class in the NetworkCapabilities.TransportInfo implies that the network is a
+ * VCN.
+ *
+ * <p>VcnTransportInfo must exist on top of either an underlying Wifi or Cellular Network. If the
+ * underlying Network is WiFi, the subId will be {@link
+ * SubscriptionManager#INVALID_SUBSCRIPTION_ID}. If the underlying Network is Cellular, the WifiInfo
+ * will be {@code null}.
+ *
+ * @hide
+ */
+public class VcnTransportInfo implements TransportInfo, Parcelable {
+ @Nullable private final WifiInfo mWifiInfo;
+ private final int mSubId;
+
+ public VcnTransportInfo(@NonNull WifiInfo wifiInfo) {
+ this(wifiInfo, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ }
+
+ public VcnTransportInfo(int subId) {
+ this(null /* wifiInfo */, subId);
+ }
+
+ private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId) {
+ if (wifiInfo == null && subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ throw new IllegalArgumentException(
+ "VcnTransportInfo requires either non-null WifiInfo or valid subId");
+ }
+
+ mWifiInfo = wifiInfo;
+ mSubId = subId;
+ }
+
+ /**
+ * Get the {@link WifiInfo} for this VcnTransportInfo.
+ *
+ * <p>If the underlying Network for the associated VCN is Cellular, returns null.
+ *
+ * @return the WifiInfo if there is an underlying WiFi connection, else null.
+ */
+ @Nullable
+ public WifiInfo getWifiInfo() {
+ return mWifiInfo;
+ }
+
+ /**
+ * Get the subId for the VCN Network associated with this VcnTransportInfo.
+ *
+ * <p>If the underlying Network for the associated VCN is WiFi, returns {@link
+ * SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+ *
+ * @return the Subscription ID if a cellular underlying Network is present, else {@link
+ * android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID}.
+ */
+ public int getSubId() {
+ return mSubId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mWifiInfo, mSubId);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof VcnTransportInfo)) return false;
+ final VcnTransportInfo that = (VcnTransportInfo) o;
+
+ return Objects.equals(mWifiInfo, that.mWifiInfo) && mSubId == that.mSubId;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {}
+
+ /** Implement the Parcelable interface */
+ public static final @NonNull Creator<VcnTransportInfo> CREATOR =
+ new Creator<VcnTransportInfo>() {
+ public VcnTransportInfo createFromParcel(Parcel in) {
+ // return null instead of a default VcnTransportInfo to avoid leaking
+ // information about this being a VCN Network (instead of macro cellular, etc)
+ return null;
+ }
+
+ public VcnTransportInfo[] newArray(int size) {
+ return new VcnTransportInfo[size];
+ }
+ };
+}
diff --git a/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 46ad7b8..305c686 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -22,11 +22,11 @@
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;
import android.content.Context;
-import android.os.Handler;
import android.util.Log;
import android.widget.Toast;
@@ -41,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 {
@@ -61,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;
@@ -100,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) {}
@@ -114,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() {}
/**
@@ -138,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,
@@ -165,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,13 +192,64 @@
}
}
- /*
- * Cancels a currently running bugreport.
+ /**
+ * 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.
*/
- @RequiresPermission(android.Manifest.permission.DUMP)
+ @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
+ */
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
public void cancelBugreport() {
try {
- mBinder.cancelBugreport();
+ mBinder.cancelBugreport(-1 /* callingUid */, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -205,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();
}
@@ -232,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;
@@ -243,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);
}
@@ -255,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);
}
@@ -267,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);
}
@@ -284,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/PatternMatcher.java b/core/java/android/os/PatternMatcher.java
index 428405b..631c98a 100644
--- a/core/java/android/os/PatternMatcher.java
+++ b/core/java/android/os/PatternMatcher.java
@@ -61,6 +61,12 @@
*/
public static final int PATTERN_ADVANCED_GLOB = 3;
+ /**
+ * Pattern type: the given pattern must match the
+ * end of the string it is tested against.
+ */
+ public static final int PATTERN_SUFFIX = 4;
+
// token types for advanced matching
private static final int TOKEN_TYPE_LITERAL = 0;
private static final int TOKEN_TYPE_ANY = 1;
@@ -128,6 +134,9 @@
case PATTERN_ADVANCED_GLOB:
type = "ADVANCED: ";
break;
+ case PATTERN_SUFFIX:
+ type = "SUFFIX: ";
+ break;
}
return "PatternMatcher{" + type + mPattern + "}";
}
@@ -179,6 +188,8 @@
return matchGlobPattern(pattern, match);
} else if (type == PATTERN_ADVANCED_GLOB) {
return matchAdvancedPattern(parsedPattern, match);
+ } else if (type == PATTERN_SUFFIX) {
+ return match.endsWith(pattern);
}
return false;
}
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/PermGroupUsage.java b/core/java/android/permission/PermGroupUsage.java
index 7335e00..c94c0ff 100644
--- a/core/java/android/permission/PermGroupUsage.java
+++ b/core/java/android/permission/PermGroupUsage.java
@@ -79,7 +79,7 @@
@Override
public String toString() {
return getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(this))
- + "packageName: " + mPackageName + ", UID: " + mUid + ", permGroup: "
- + mPermGroupName + ", isActive: " + mIsActive + ",attribution: " + mAttribution;
+ + " packageName: " + mPackageName + ", UID: " + mUid + ", permGroup: "
+ + mPermGroupName + ", isActive: " + mIsActive + ", attribution: " + mAttribution;
}
}
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 00ba867..c28b59b 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -31,14 +31,26 @@
import android.annotation.NonNull;
import android.app.AppOpsManager;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.Attribution;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.icu.text.ListFormatter;
import android.location.LocationManager;
import android.os.Process;
import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.speech.RecognitionService;
+import android.speech.RecognizerIntent;
import android.util.ArrayMap;
-import android.util.Pair;
+import android.util.ArraySet;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
import java.util.ArrayList;
import java.util.Collections;
@@ -69,6 +81,9 @@
/** How long after an access to show it as "running" */
private static final String RUNNING_ACCESS_TIME_MS = "running_acccess_time_ms";
+ /** The name of the expected voice IME subtype */
+ private static final String VOICE_IME_SUBTYPE = "voice";
+
private static final long DEFAULT_RUNNING_TIME_MS = 5000L;
private static final long DEFAULT_RECENT_TIME_MS = 30000L;
@@ -168,13 +183,25 @@
}
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++) {
boolean isPhone = false;
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<>();
+ }
+
+ List<OpUsage> permUsages = removeDuplicatesAndProxies(rawUsages.get(permGroup),
+ pkgAttrLabels.keySet(), proxies);
+
if (permGroup.equals(OPSTR_PHONE_CALL_MICROPHONE)) {
isPhone = true;
permGroup = MICROPHONE;
@@ -183,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())));
@@ -256,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);
@@ -291,77 +317,374 @@
return flattenedUsages;
}
- // TODO ntmyren: create JavaDoc and copy merging of proxy chains and trusted labels from
- // "usages" livedata in ReviewOngoingUsageLiveData
- private Map<PackageAttribution, CharSequence> getTrustedAttributions(List<OpUsage> usages) {
+ /**
+ * Take the list of all usages, figure out any proxy chains, get all possible special
+ * attribution labels, and figure out which usages need to show a special label, if any.
+ *
+ * @param usages The raw permission usages
+ *
+ * @return A map of package + attribution (in the form of a PackageAttribution object) to
+ * trusted attribution label, if there is one
+ */
+ private ArrayMap<PackageAttribution, CharSequence> getTrustedAttributions(
+ List<OpUsage> usages, Set<List<PackageAttribution>> proxyChains) {
ArrayMap<PackageAttribution, CharSequence> attributions = new ArrayMap<>();
if (usages == null) {
return attributions;
}
- Set<List<OpUsage>> proxyChains = getProxyChains(usages);
- Map<Pair<String, UserHandle>, CharSequence> trustedLabels = getTrustedAttributionLabels();
+ 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) {
+ continue;
+ }
+
+ // If the last link in the chain is not user sensitive, do not show it.
+ boolean lastLinkIsUserSensitive = false;
+ for (int i = 0; i < usages.size(); i++) {
+ PackageAttribution lastLink = chain.get(chain.size() - 1);
+ if (lastLink.equals(usages.get(i).toPackageAttr())) {
+ lastLinkIsUserSensitive = true;
+ break;
+ }
+ }
+ if (!lastLinkIsUserSensitive) {
+ continue;
+ }
+
+ List<CharSequence> labels = new ArrayList<>();
+ for (int i = 0; i < chain.size(); i++) {
+ // If this is the last link in the proxy chain, assign it the series of labels
+ // Else, if it has a special label, add that label
+ // Else, if there are no other apps in the remaining part of the chain which
+ // have the same package name, add the app label
+ // If it is not the last link in the chain, remove its attribution
+ PackageAttribution attr = chain.get(i);
+ CharSequence trustedLabel = trustedLabels.get(attr);
+ if (i == chain.size() - 1) {
+ attributions.put(attr, formatLabelList(labels));
+ } else if (trustedLabel != null && !labels.contains(trustedLabel)) {
+ labels.add(trustedLabel);
+ trustedLabels.remove(attr);
+ } else {
+ boolean remainingChainHasPackage = false;
+ for (int attrNum = i + 1; attrNum < chain.size() - 1; attrNum++) {
+ if (chain.get(i).packageName.equals(attr.packageName)) {
+ remainingChainHasPackage = true;
+ break;
+ }
+ }
+ if (!remainingChainHasPackage) {
+ try {
+ ApplicationInfo appInfo = mPkgManager.getApplicationInfoAsUser(
+ attr.packageName, 0, attr.getUser());
+ CharSequence appLabel = appInfo.loadLabel(
+ getUserContext(attr.getUser()).getPackageManager());
+ labels.add(appLabel);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Do nothing
+ }
+ }
+ }
+ }
+ }
+
+ for (PackageAttribution attr : trustedLabels.keySet()) {
+ attributions.put(attr, trustedLabels.get(attr));
+ }
return attributions;
}
- // TODO ntmyren: create JavaDoc and copy proxyChainsLiveData from ReviewOngoingUsageLiveData
- private Set<List<OpUsage>> getProxyChains(List<OpUsage> usages) {
- Map<PackageAttribution, List<OpUsage>> inProgressChains = new ArrayMap<>();
- List<OpUsage> remainingUsages = new ArrayList<>(usages);
- // find all one-link chains (that is, all proxied apps whose proxy is not included in
- // the usage list)
- for (int usageNum = 0; usageNum < usages.size(); usageNum++) {
- OpUsage usage = usages.get(usageNum);
- PackageAttribution usageAttr = usage.toPackageAttr();
- if (usage.proxy == null) {
- continue;
- }
- PackageAttribution proxyAttr = usage.proxy.toPackageAttr();
- boolean proxyExists = false;
- for (int otherUsageNum = 0; otherUsageNum < usages.size(); otherUsageNum++) {
- if (usages.get(otherUsageNum).toPackageAttr().equals(proxyAttr)) {
- proxyExists = true;
- break;
- }
- }
-
- if (!proxyExists) {
- inProgressChains.put(usageAttr, List.of(usage));
- remainingUsages.remove(usage);
- }
- }
-
- // find all possible starting points for chains
- for (int i = 0; i < usages.size(); i++) {
- OpUsage usage = usages.get(i);
- }
-
- /*
- // find all possible starting points for chains
- for (usage in remainingProxyChainUsages.toList()) {
- // if this usage has no proxy, but proxies another usage, it is the start of a chain
- val usageAttr = getPackageAttr(usage)
- if (usage.proxyAccess == null && remainingProxyChainUsages.any {
- it.proxyAccess != null && getPackageAttr(it.proxyAccess) == usageAttr
- }) {
- inProgressChains[usageAttr] = mutableListOf(usage)
- }
-
- // if this usage is a chain start, or no usage have this usage as a proxy, remove it
- if (usage.proxyAccess == null) {
- remainingProxyChainUsages.remove(usage)
- }
- }
-
- */
-
- return null;
+ private CharSequence formatLabelList(List<CharSequence> labels) {
+ return ListFormatter.getInstance().format(labels);
}
- // TODO ntmyren: create JavaDoc and copy trustedAttrsLiveData from ReviewOngoingUsageLiveData
- private Map<Pair<String, UserHandle>, CharSequence> getTrustedAttributionLabels() {
- return new ArrayMap<>();
+ /**
+ * Get all chains of proxy usages. A proxy chain is defined as one usage at the root, then
+ * further proxy usages, where the app and attribution tag of the proxy in the proxy usage
+ * matches the previous usage in the chain.
+ *
+ * @param usages The permission usages
+ *
+ * @return A set of lists of package attributions. One list represents a chain of proxy usages,
+ * with the start of the chain (the usage without a proxy) at position 0, and each usage down
+ * the chain has the previous one listed as a proxy usage.
+ */
+ private Set<List<PackageAttribution>> getProxyChains(List<OpUsage> usages) {
+ 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 < 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 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));
+ if (usage == null) {
+ continue;
+ }
+ PackageAttribution usageAttr = usage.toPackageAttr();
+ // 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) {
+ 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 in reverse order, then invert them
+ for (int numStart = 0; numStart < proxyChains.size(); numStart++) {
+ PackageAttribution currPackageAttr = proxyChains.keyAt(numStart);
+ ArrayList<PackageAttribution> proxyChain = proxyChains.get(currPackageAttr);
+ OpUsage currentUsage = remainingUsages.get(currPackageAttr);
+ if (currentUsage == null || proxyChain == null) {
+ continue;
+ }
+ while (currentUsage.proxy != null) {
+ currPackageAttr = currentUsage.proxy.toPackageAttr();
+ currentUsage = remainingUsages.get(currPackageAttr);
+
+ boolean invalidState = false;
+ for (int chainNum = 0; chainNum < proxyChain.size(); chainNum++) {
+ if (currentUsage == null || proxyChain.get(chainNum).equals(currPackageAttr)) {
+ // either our current value is not in the usage list, or we have a cycle
+ invalidState = true;
+ break;
+ }
+ }
+
+ if (invalidState) {
+ break;
+ }
+
+ 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());
+ }
+
+ /**
+ * Gets all trusted proxied voice IME and voice recognition microphone uses, and get the
+ * label needed to display with it, as well as information about the proxy whose label is being
+ * shown, if applicable.
+ *
+ * @param usages The permission usages
+ *
+ * @return A map of package attribution -> the attribution label for that package attribution,
+ * if applicable
+ */
+ private Map<PackageAttribution, CharSequence> getTrustedAttributionLabels(
+ List<OpUsage> usages) {
+ List<UserHandle> users = new ArrayList<>();
+ for (int i = 0; i < usages.size(); i++) {
+ UserHandle user = UserHandle.getUserHandleForUid(usages.get(i).uid);
+ if (!users.contains(user)) {
+ users.add(user);
+ }
+ }
+
+ Map<PackageAttribution, CharSequence> trustedLabels = new ArrayMap<>();
+ for (int userNum = 0; userNum < users.size(); userNum++) {
+ UserHandle user = users.get(userNum);
+ Context userContext = mContext.createContextAsUser(user, 0);
+
+ // Get all voice IME labels
+ Map<String, CharSequence> voiceInputs = new ArrayMap<>();
+ List<InputMethodInfo> inputs = userContext.getSystemService(InputMethodManager.class)
+ .getEnabledInputMethodList();
+ for (int inputNum = 0; inputNum < inputs.size(); inputNum++) {
+ InputMethodInfo input = inputs.get(inputNum);
+ for (int subtypeNum = 0; subtypeNum < input.getSubtypeCount(); subtypeNum++) {
+ if (VOICE_IME_SUBTYPE.equals(input.getSubtypeAt(subtypeNum).getMode())) {
+ voiceInputs.put(input.getPackageName(), input.getServiceInfo()
+ .loadUnsafeLabel(userContext.getPackageManager()));
+ break;
+ }
+ }
+ }
+
+ // Get the currently selected recognizer from the secure setting
+ String recognitionPackageName = Settings.Secure.getString(
+ userContext.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE);
+ if (recognitionPackageName == null) {
+ continue;
+ }
+ recognitionPackageName =
+ ComponentName.unflattenFromString(recognitionPackageName).getPackageName();
+ Map<String, CharSequence> recognizers = new ArrayMap<>();
+ List<ResolveInfo> availableRecognizers = mPkgManager.queryIntentServicesAsUser(
+ new Intent(RecognitionService.SERVICE_INTERFACE), PackageManager.GET_META_DATA,
+ user.getIdentifier());
+ for (int recogNum = 0; recogNum < availableRecognizers.size(); recogNum++) {
+ ResolveInfo info = availableRecognizers.get(recogNum);
+ if (recognitionPackageName.equals(info.serviceInfo.packageName)) {
+ recognizers.put(recognitionPackageName, info.serviceInfo.loadUnsafeLabel(
+ userContext.getPackageManager()));
+ }
+ }
+
+ Map<String, CharSequence> recognizerIntents = new ArrayMap<>();
+ List<ResolveInfo> availableRecognizerIntents = mPkgManager.queryIntentActivitiesAsUser(
+ new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH),
+ PackageManager.GET_META_DATA, user);
+ for (int recogNum = 0; recogNum < availableRecognizerIntents.size(); recogNum++) {
+ ResolveInfo info = availableRecognizerIntents.get(recogNum);
+ if (info.activityInfo == null) {
+ continue;
+ }
+ String pkgName = info.activityInfo.packageName;
+ if (recognitionPackageName.equals(pkgName) && recognizers.containsKey(pkgName)) {
+ recognizerIntents.put(pkgName, recognizers.get(pkgName));
+ }
+ }
+ for (int usageNum = 0; usageNum < usages.size(); usageNum++) {
+ setTrustedAttrsForAccess(usages.get(usageNum), user, false, voiceInputs,
+ trustedLabels);
+ setTrustedAttrsForAccess(usages.get(usageNum), user, false, recognizerIntents,
+ trustedLabels);
+ setTrustedAttrsForAccess(usages.get(usageNum), user, true, recognizers,
+ trustedLabels);
+ }
+ }
+
+ return trustedLabels;
+ }
+
+ private void setTrustedAttrsForAccess(OpUsage opUsage, UserHandle currUser, boolean getProxy,
+ Map<String, CharSequence> trustedMap, Map<PackageAttribution, CharSequence> toSetMap) {
+ OpUsage usage = opUsage;
+ if (getProxy) {
+ usage = opUsage.proxy;
+ }
+
+ if (usage == null || !usage.getUser().equals(currUser)
+ || !trustedMap.containsKey(usage.packageName)) {
+ return;
+ }
+
+ CharSequence label = getAttributionLabel(usage);
+ if (trustedMap.get(usage.packageName).equals(label)) {
+ toSetMap.put(opUsage.toPackageAttr(), label);
+ }
+ }
+
+ private CharSequence getAttributionLabel(OpUsage usage) {
+ if (usage.attributionTag == null) {
+ return null;
+ }
+
+ PackageInfo pkgInfo;
+ try {
+ pkgInfo = mPkgManager.getPackageInfoAsUser(usage.packageName,
+ PackageManager.GET_ATTRIBUTIONS, usage.getUser().getIdentifier());
+ if (pkgInfo.attributions == null || pkgInfo.attributions.length == 0) {
+ return null;
+ }
+ for (int attrNum = 0; attrNum < pkgInfo.attributions.length; attrNum++) {
+ Attribution attr = pkgInfo.attributions[attrNum];
+ if (usage.attributionTag.equals(attr.getTag())) {
+ return mContext.createPackageContextAsUser(usage.packageName, 0,
+ usage.getUser()).getString(attr.getLabel());
+ }
+ }
+ return null;
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ }
+
+ /**
+ * 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 deDuped;
+ }
+
+ List<PackageAttribution> toRemoveProxies = new ArrayList<>();
+ for (List<PackageAttribution> proxyList: proxies) {
+ PackageAttribution lastLink = proxyList.get(proxyList.size() - 1);
+ if (!specialAttributions.contains(lastLink)) {
+ continue;
+ }
+ for (int proxyNum = 0; proxyNum < proxyList.size(); proxyNum++) {
+ if (!proxyList.get(proxyNum).equals(lastLink)) {
+ toRemoveProxies.add(proxyList.get(proxyNum));
+ }
+ }
+ }
+
+ for (int usageNum = 0; usageNum < rawUsages.size(); usageNum++) {
+ 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) {
@@ -407,6 +730,10 @@
public PackageAttribution toPackageAttr() {
return new PackageAttribution(packageName, attributionTag, uid);
}
+
+ public UserHandle getUser() {
+ return UserHandle.getUserHandleForUid(uid);
+ }
}
/**
@@ -438,5 +765,9 @@
public int hashCode() {
return Objects.hash(packageName, attributionTag, uid);
}
+
+ public UserHandle getUser() {
+ return UserHandle.getUserHandleForUid(uid);
+ }
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9db7ca0..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.
@@ -8440,14 +8454,6 @@
"emergency_gesture_sound_enabled";
/**
- * The default number to call in emergency gesture
- *
- * @hide
- */
- public static final String EMERGENCY_GESTURE_CALL_NUMBER =
- "emergency_gesture_call_number";
-
- /**
* Whether the camera launch gesture to double tap the power button when the screen is off
* should be disabled.
*
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 4842aae..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;
@@ -17914,6 +17914,7 @@
* @see #setOutlineProvider(ViewOutlineProvider)
* @see #getClipToOutline()
*/
+ @RemotableViewMethod
public void setClipToOutline(boolean clipToOutline) {
damageInParent();
if (getClipToOutline() != clipToOutline) {
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 7f639ff..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
@@ -2131,6 +2141,16 @@
*/
public static final int PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS = 0x00000004;
+ /**
+ * When set {@link LayoutParams#TYPE_APPLICATION_OVERLAY} windows will stay visible, even if
+ * {@link LayoutParams#SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS} is set for another
+ * visible window.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(permission.SYSTEM_APPLICATION_OVERLAY)
+ public static final int SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY = 0x00000008;
+
/** In a multiuser system if this flag is set and the owner is a system process then this
* window will appear on all user screens. This overrides the default behavior of window
* types that normally only appear on the owning user's screen. Refer to each window type
@@ -2328,6 +2348,7 @@
@IntDef(flag = true, prefix = { "SYSTEM_FLAG_" }, value = {
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
+ SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY,
})
public @interface SystemFlags {}
@@ -2361,6 +2382,7 @@
PRIVATE_FLAG_TRUSTED_OVERLAY,
PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME,
PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP,
+ SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY,
})
public @interface PrivateFlags {}
@@ -2473,7 +2495,11 @@
@ViewDebug.FlagToString(
mask = PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP,
equals = PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP,
- name = "INTERCEPT_GLOBAL_DRAG_AND_DROP")
+ name = "INTERCEPT_GLOBAL_DRAG_AND_DROP"),
+ @ViewDebug.FlagToString(
+ mask = SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY,
+ equals = SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY,
+ name = "SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY")
})
@PrivateFlags
@TestApi
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/OWNERS b/core/java/android/view/textclassifier/OWNERS
index ac80d9f..4bcdeea 100644
--- a/core/java/android/view/textclassifier/OWNERS
+++ b/core/java/android/view/textclassifier/OWNERS
@@ -6,3 +6,5 @@
svetoslavganov@google.com
augale@google.com
joannechung@google.com
+tonymak@google.com
+licha@google.com
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index 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/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 34e03af..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";
@@ -218,6 +218,7 @@
public static final int SELECTION_TYPE_STANDARD = 3;
public static final int SELECTION_TYPE_COPY = 4;
public static final int SELECTION_TYPE_NEARBY = 5;
+ public static final int SELECTION_TYPE_EDIT = 6;
private static final int SCROLL_STATUS_IDLE = 0;
private static final int SCROLL_STATUS_SCROLLING_VERTICAL = 1;
@@ -1197,6 +1198,37 @@
}
@VisibleForTesting
+ protected @Nullable ComponentName getEditSharingComponent() {
+ String editorPackage = getApplicationContext().getString(R.string.config_systemImageEditor);
+ if (editorPackage == null || TextUtils.isEmpty(editorPackage)) {
+ return null;
+ }
+ return ComponentName.unflattenFromString(editorPackage);
+ }
+
+ @VisibleForTesting
+ protected TargetInfo getEditSharingTarget(Intent originalIntent) {
+ final ComponentName cn = getEditSharingComponent();
+
+ final Intent resolveIntent = new Intent(originalIntent);
+ resolveIntent.setComponent(cn);
+ resolveIntent.setAction(Intent.ACTION_EDIT);
+ final ResolveInfo ri = getPackageManager().resolveActivity(
+ resolveIntent, PackageManager.GET_META_DATA);
+ if (ri == null || ri.activityInfo == null) {
+ Log.e(TAG, "Device-specified image edit component (" + cn
+ + ") not available");
+ return null;
+ }
+
+ final DisplayResolveInfo dri = new DisplayResolveInfo(
+ originalIntent, ri, getString(R.string.screenshot_edit), "", resolveIntent, null);
+ dri.setDisplayIcon(getDrawable(R.drawable.ic_menu_edit));
+ return dri;
+ }
+
+
+ @VisibleForTesting
protected TargetInfo getNearbySharingTarget(Intent originalIntent) {
final ComponentName cn = getNearbySharingComponent();
if (cn == null) return null;
@@ -1282,6 +1314,27 @@
return b;
}
+ private @Nullable Button createEditButton(Intent originalIntent) {
+ final TargetInfo ti = getEditSharingTarget(originalIntent);
+ if (ti == null) return null;
+
+ final Button b = createActionButton(
+ ti.getDisplayIcon(this),
+ ti.getDisplayLabel(),
+ (View unused) -> {
+ // Log share completion via edit
+ getChooserActivityLogger().logShareTargetSelected(
+ SELECTION_TYPE_EDIT,
+ "",
+ -1);
+ safelyStartActivity(ti);
+ finish();
+ }
+ );
+ b.setId(R.id.chooser_edit_button);
+ return b;
+ }
+
private void addActionButton(ViewGroup parent, Button b) {
if (b == null) return;
final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(
@@ -1378,6 +1431,7 @@
(ViewGroup) contentPreviewLayout.findViewById(R.id.chooser_action_row);
//TODO: addActionButton(actionRow, createCopyButton());
addActionButton(actionRow, createNearbyButton(targetIntent));
+ addActionButton(actionRow, createEditButton(targetIntent));
mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, false);
diff --git a/core/java/com/android/internal/app/ChooserActivityLogger.java b/core/java/com/android/internal/app/ChooserActivityLogger.java
index 426859e..47d8334 100644
--- a/core/java/com/android/internal/app/ChooserActivityLogger.java
+++ b/core/java/com/android/internal/app/ChooserActivityLogger.java
@@ -118,7 +118,9 @@
@UiEvent(doc = "User selected the copy target.")
SHARESHEET_COPY_TARGET_SELECTED(235),
@UiEvent(doc = "User selected the nearby target.")
- SHARESHEET_NEARBY_TARGET_SELECTED(626);
+ SHARESHEET_NEARBY_TARGET_SELECTED(626),
+ @UiEvent(doc = "User selected the edit target.")
+ SHARESHEET_EDIT_TARGET_SELECTED(669);
private final int mId;
SharesheetTargetSelectedEvent(int id) {
@@ -140,6 +142,8 @@
return SHARESHEET_COPY_TARGET_SELECTED;
case ChooserActivity.SELECTION_TYPE_NEARBY:
return SHARESHEET_NEARBY_TARGET_SELECTED;
+ case ChooserActivity.SELECTION_TYPE_EDIT:
+ return SHARESHEET_EDIT_TARGET_SELECTED;
default:
return INVALID;
}
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/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS
index cca39ea..ae566c3 100644
--- a/core/java/com/android/internal/widget/OWNERS
+++ b/core/java/com/android/internal/widget/OWNERS
@@ -1 +1,7 @@
per-file PointerLocationView.java = michaelwr@google.com, svv@google.com
+
+# LockSettings related
+per-file *LockPattern* = file:/services/core/java/com/android/server/locksettings/OWNERS
+per-file *LockScreen* = file:/services/core/java/com/android/server/locksettings/OWNERS
+per-file *Lockscreen* = file:/services/core/java/com/android/server/locksettings/OWNERS
+per-file *LockSettings* = file:/services/core/java/com/android/server/locksettings/OWNERS
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 302ac3c..8dc56ed 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1946,7 +1946,7 @@
jobject jRule = env->GetObjectField(jAudioMix, gAudioMixFields.mRule);
jobject jRuleCriteria = env->GetObjectField(jRule, gAudioMixingRuleFields.mCriteria);
- nAudioMix->mAllowPrivilegedPlaybackCapture =
+ nAudioMix->mAllowPrivilegedMediaPlaybackCapture =
env->GetBooleanField(jRule, gAudioMixingRuleFields.mAllowPrivilegedPlaybackCapture);
nAudioMix->mVoiceCommunicationCaptureAllowed =
env->GetBooleanField(jRule, gAudioMixingRuleFields.mVoiceCommunicationCaptureAllowed);
diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp
index 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 05fbe4b..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,24 @@
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},
+ shown on top of all other apps.
+
+ Allows an application to use
+ {@link android.view.WindowManager.LayoutsParams#SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY}
+ to create overlays that will stay visible, even if another window is requesting overlays to
+ be hidden through {@link android.view.Window#setHideOverlayWindows(boolean)}.
+
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.SYSTEM_APPLICATION_OVERLAY"
+ android:protectionLevel="signature|wellbeing"/>
<!-- @deprecated Use {@link android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND}
@hide
@@ -3092,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 -->
@@ -3147,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 -->
<!-- ========================================= -->
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index cef5e1c..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Bel oor Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Bel oor mobiele netwerk"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Net Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 0d3939b..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"በ Wi-Fi በኩል ደውል"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"ከተንቀሳቃሽ ስልክ አውታረ መረብ በኩል ደውል"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi ብቻ"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 3a4518e..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>
@@ -150,6 +151,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"الاتصال عبر Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"الاتصال عبر شبكة الجوّال"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi فقط"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 3adb318..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"ৱাই-ফাইৰ জৰিয়তে কল কৰক"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"ম’বাইল নেটৱৰ্কৰ জৰিয়তে কল কৰক"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"কোৱল ৱাই-ফাই"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 a223715..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Wi-Fi ilə zəng edin"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Mobil şəbəkə ilə zəng edin"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Yalnız Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 aa7f658..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>
@@ -147,6 +148,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Pozivanje preko WiFi-a"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Poziv preko mobilne mreže"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo WiFi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 8cc58a1..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>
@@ -148,6 +149,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Выклікаць праз Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Выклікаць праз мабільную сетку"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Толькі Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 ec29f3e..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Обаждане през Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Обаждане през мобилна мрежа"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Само Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 8dced4b2..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"ওয়াই-ফাইয়ের মাধ্যমে কল করুন"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"মোবাইল নেটওয়ার্কের মাধ্যমে কল করুন"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"শুধুমাত্র ওয়াই-ফাই"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 64b785a..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>
@@ -147,6 +148,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Poziv putem WiFi-ja"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Poziv putem mobilne mreže"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo WiFi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 17a5039..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Trucades per Wi‑Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Trucades per la xarxa mòbil"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Només Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 e51ec22..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>
@@ -148,6 +149,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Volání přes Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Volání přes mobilní síť"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Pouze Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> – volání napříč SIM kartami"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nepřesměrováno"</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-da/strings.xml b/core/res/res/values-da/strings.xml
index 19add07..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Ring via Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Ring via mobilnetværk"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Kun Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 410b597..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>
@@ -146,6 +147,10 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Anruf über WLAN"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Über Mobilfunknetz anrufen"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Nur WLAN"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <skip />
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nicht weitergeleitet"</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> nach <xliff:g id="TIME_DELAY">{2}</xliff:g> Sekunden."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 7427216..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Κλήση μέσω Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Κλήση μέσω δικτύου κινητής τηλεφωνίας"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Μόνο Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 ae324be..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Call over Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Call over mobile network"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi only"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 630a243..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Call over Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Call over mobile network"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi only"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 1a8f5c0..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Call over Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Call over mobile network"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi only"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 6503267..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Call over Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Call over mobile network"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi only"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 0d0b204..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Call over Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Call over mobile network"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi only"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 1128479..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Llamar mediante Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Llamar mediante red móvil"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Solo Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 366c506..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Llamar a través de la red Wi‑Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Llamar a través de la red móvil"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Solo Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 d585452..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Helista WiFi kaudu"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Helista mobiilsidevõrgu kaudu"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Ainult WiFi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 4ba95bb..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Deitu wifi bidez"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Deitu sare mugikorraren bidez"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wifi-sarea soilik"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 4c75ac5..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"تماس ازطریق Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"تماس ازطریق شبکه تلفن همراه"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"فقط Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 93b5ff1..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Soita Wi-Fin kautta"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Soita mobiiliverkon kautta"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Vain Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 d3329d3..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Appels par Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Appels sur réseau cellulaire"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi seulement"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 6c07066..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Appel via le Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Appel via le réseau mobile"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi uniquement"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 cab106ac..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Chama por wifi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Chama pola rede de telefonía móbil"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Só por wifi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 016e77b..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>
@@ -146,6 +147,10 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"વાઇ-ફાઇ પરથી કૉલ કરો"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"મોબાઇલ નેટવર્ક પરથી કૉલ કરો"</string>
<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="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="TIME_DELAY">{2}</xliff:g> સેકન્ડ પછી <xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index a8afbbe..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"वाई-फ़ाई के ज़रिए कॉल करें"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"मोबाइल नेटवर्क के ज़रिए कॉल"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"केवल वाई-फ़ाई"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 625b3ed..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>
@@ -147,6 +148,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Pozivi putem Wi-Fija"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Pozivi putem mobilne mreže"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 551043e..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Hívás Wi-Fi-hálózaton"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Hívás mobilhálózaton"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Csak Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 f08c7f9..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Զանգ Wi-Fi-ի միջոցով"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Զանգ բջջային ցանցի միջոցով"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Միայն Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 a7a7cb8..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Panggilan telepon melalui Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Panggilan telepon melalui jaringan seluler"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Khusus Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 2ca613c..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Hringja í gegnum Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Hringja í gegnum farsímakerfi"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi eingöngu"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 4f7eea8..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Chiamata tramite Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Chiamata su rete mobile"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Solo Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 be3e3c75..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>
@@ -148,6 +149,10 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"שיחה בחיבור Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"שיחה ברשת סלולרית"</string>
<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="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-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 658237d..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Wi-Fi 経由で通話"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"モバイル ネットワーク経由で通話"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fiのみ"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 6d9c12b..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"დარეკვა Wi-Fi-ის მეშვეობით"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"დარეკვა მობილური ქსელის მეშვეობით"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"მხოლოდ Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 d57aca5..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Wi-Fi арқылы қоңырау шалу"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Мобильдік желі арқылы қоңырау шалу"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Тек Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 362b7c0..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"ហៅទូរសព្ទតាមរយៈ Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"ហៅទូរសព្ទតាមរយៈបណ្តាញទូរសព្ទចល័ត"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi តែប៉ុណ្ណោះ"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 b7356e1..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>
@@ -146,6 +147,10 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"ವೈ-ಫೈ ಬಳಸಿ ಕರೆ ಮಾಡಿ"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"ಮೊಬೈಲ್ ನೆಟ್ವರ್ಕ್ ಬಳಸಿ ಕರೆ ಮಾಡಿ"</string>
<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="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-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 31837a3..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Wi-Fi를 통해 통화"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"모바일 네트워크를 통해 통화"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi에서만"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 4211bed..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Wi-Fi аркылуу чалуу"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Мобилдик тармак аркылуу чалуу"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi гана"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 0646254..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"ໂທຜ່ານ Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"ໂທຜ່ານເຄືອຂ່າຍມືຖື"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi ເທົ່ານັ້ນ"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 d83506e..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>
@@ -148,6 +149,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Skambinimas naudojant „Wi-Fi“"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Skambinimas naudojant mobiliojo ryšio tinklą"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Tik „Wi-Fi“"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 68609ce..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>
@@ -147,6 +148,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Zvani Wi-Fi tīklā"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Zvani mobilajā tīklā"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Tikai Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 c41b56c..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Повикувај преку Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Повикувај преку мобилна мрежа"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Само Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 30c0d03..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"വൈഫൈ മുഖേനയുള്ള കോൾ"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"മൊബൈൽ നെറ്റ്വർക്ക് മുഖേനയുള്ള കോൾ"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"വൈഫൈ മാത്രം"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 2966035..778a15b 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -49,15 +49,16 @@
<string name="invalidPuk" msgid="8831151490931907083">"8-с цөөнгүй тооноос бүтэх PUK-г бичнэ үү."</string>
<string name="needPuk" msgid="7321876090152422918">"SIM картны PUK-түгжигдсэн. Тайлах бол PUK кодыг бичнэ үү."</string>
<string name="needPuk2" msgid="7032612093451537186">"SIM картыг блокоос гаргах бол PUK2-г бичнэ үү."</string>
- <string name="enablePin" msgid="2543771964137091212">"Амжилтгүй боллоо, СИМ/РҮИМ түгжээг идэвхжүүлнэ үү."</string>
+ <string name="enablePin" msgid="2543771964137091212">"Амжилтгүй боллоо, SIM/РҮИМ түгжээг идэвхжүүлнэ үү."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
- <item quantity="other">Таны СИМ түгжигдэхээс өмнө танд <xliff:g id="NUMBER_1">%d</xliff:g> оролдлого хийх боломж үлдлээ. </item>
- <item quantity="one">Таны СИМ түгжигдэхээс өмнө танд <xliff:g id="NUMBER_0">%d</xliff:g> оролдлого хийх боломж үлдлээ. </item>
+ <item quantity="other">Таны SIM түгжигдэхээс өмнө танд <xliff:g id="NUMBER_1">%d</xliff:g> оролдлого хийх боломж үлдлээ. </item>
+ <item quantity="one">Таны SIM түгжигдэхээс өмнө танд <xliff:g id="NUMBER_0">%d</xliff:g> оролдлого хийх боломж үлдлээ. </item>
</plurals>
<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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Wi-Fi-р залгах"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Мобайл сүлжээгээр дуудлага хийх"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Зөвхөн Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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>
@@ -809,7 +813,7 @@
<string name="relationTypeAssistant" msgid="4057605157116589315">"Туслагч"</string>
<string name="relationTypeBrother" msgid="7141662427379247820">"Ах"</string>
<string name="relationTypeChild" msgid="9076258911292693601">"Хүүхэд"</string>
- <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"Дотоод Түнш"</string>
+ <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"Хамтран амьдрагч"</string>
<string name="relationTypeFather" msgid="3856225062864790596">"Эцэг"</string>
<string name="relationTypeFriend" msgid="3192092625893980574">"Найз"</string>
<string name="relationTypeManager" msgid="2272860813153171857">"Менежер"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index b65a316..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>
@@ -146,6 +147,10 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"वाय-फायवरून कॉल करा"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"मोबाइल नेटवर्कवरून कॉल करा"</string>
<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="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>
@@ -394,9 +399,9 @@
<string name="permlab_writeSettings" msgid="8057285063719277394">"सिस्टम सेटिंग्ज सुधारित करा"</string>
<string name="permdesc_writeSettings" msgid="8293047411196067188">"सिस्टीमचा सेटिंग्ज डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स आपल्या सिस्टीमचे कॉंफिगरेशन दूषित करू शकतात."</string>
<string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"सुरूवातीस चालवा"</string>
- <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"जसे सिस्टम बूट करणे समाप्त करते तसे अॅप ला स्वतः प्रारंभ करण्यास अनुमती देते. यामुळे टॅबलेट प्रारंभ करण्यास वेळ लागू शकतो आणि नेहमी सुरू राहून एकंदर टॅबलेटला धीमे करण्यास अॅप ला अनुमती देते."</string>
+ <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"जसे सिस्टम बूट करणे समाप्त करते तसे अॅप ला स्वतः सुरू करण्यास अनुमती देते. यामुळे टॅबलेट सुरू करण्यास वेळ लागू शकतो आणि नेहमी सुरू राहून एकंदर टॅबलेटला धीमे करण्यास अॅप ला अनुमती देते."</string>
<string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"सिस्टम बूट होणे संपल्यावर ॲपला स्वतः सुरू होण्याची अनुमती देते. यामुळे तुमच्या Android TV डिव्हाइसला सुरू होण्यास वेळ लागू शकतो आणि नेहमी सुरू राहून एकंदर डिव्हाइसलाच धीमे करण्याची अनुमती ॲपला देते."</string>
- <string name="permdesc_receiveBootCompleted" product="default" msgid="7912677044558690092">"जसे सिस्टम बूट करणे समाप्त करते तसे अॅप ला स्वतः प्रारंभ करण्यास अनुमती देते. यामुळे फोन प्रारंभ करण्यास वेळ लागू शकतो आणि नेहमी सुरू राहून एकंदर फोनला धीमे करण्यास अॅप ला अनुमती देते."</string>
+ <string name="permdesc_receiveBootCompleted" product="default" msgid="7912677044558690092">"जसे सिस्टम बूट करणे समाप्त करते तसे अॅप ला स्वतः सुरू करण्यास अनुमती देते. यामुळे फोन सुरू करण्यास वेळ लागू शकतो आणि नेहमी सुरू राहून एकंदर फोनला धीमे करण्यास अॅप ला अनुमती देते."</string>
<string name="permlab_broadcastSticky" msgid="4552241916400572230">"रोचक प्रसारण पाठवा"</string>
<string name="permdesc_broadcastSticky" product="tablet" msgid="5058486069846384013">"रोचक प्रसारणे पाठविण्यासाठी अॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो टॅब्लेटला धीमा किंवा अस्थिर करू शकतो."</string>
<string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"चिकट प्रसारणे पाठविण्यासाठी ॲपला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो तुमच्या Android TV डिव्हाइसला धीमा किंवा अस्थिर करू शकतो."</string>
@@ -910,7 +915,7 @@
<string name="keyguard_accessibility_status" msgid="6792745049712397237">"स्थिती"</string>
<string name="keyguard_accessibility_camera" msgid="7862557559464986528">"कॅमेरा"</string>
<string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"मीडिया नियंत्रणे"</string>
- <string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"विजेट पुनर्क्रमित करणे प्रारंभ झाले."</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"विजेट पुनर्क्रमित करणे सुरू झाले."</string>
<string name="keyguard_accessibility_widget_reorder_end" msgid="1083806817600593490">"विजेट पुनर्क्रमित करणे समाप्त झाले."</string>
<string name="keyguard_accessibility_widget_deleted" msgid="1509738950119878705">"विजेट <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> हटविले."</string>
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"अनलॉक क्षेत्र विस्तृत करा."</string>
@@ -1203,7 +1208,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4268417249079938805">"टॅबलेट अपडेट होत आहे…"</string>
<string name="android_upgrading_title" product="device" msgid="6774767702998149762">"डिव्हाइस अपडेट होत आहे…"</string>
<string name="android_start_title" product="default" msgid="4036708252778757652">"फोन सुरू होत आहे…"</string>
- <string name="android_start_title" product="automotive" msgid="7917984412828168079">"Android प्रारंभ करत आहे…"</string>
+ <string name="android_start_title" product="automotive" msgid="7917984412828168079">"Android सुरू करत आहे…"</string>
<string name="android_start_title" product="tablet" msgid="4429767260263190344">"टॅबलेट सुरू होत आहे…"</string>
<string name="android_start_title" product="device" msgid="6967413819673299309">"डिव्हाइस सुरू होत आहे…"</string>
<string name="android_upgrading_fstrim" msgid="3259087575528515329">"संचयन ऑप्टिमाइझ करत आहे."</string>
@@ -1211,7 +1216,7 @@
<string name="app_upgrading_toast" msgid="1016267296049455585">"<xliff:g id="APPLICATION">%1$s</xliff:g> श्रेणीसुधारित करत आहे…"</string>
<string name="android_upgrading_apk" msgid="1339564803894466737">"<xliff:g id="NUMBER_1">%2$d</xliff:g> पैकी <xliff:g id="NUMBER_0">%1$d</xliff:g> अॅप ऑप्टिमाइझ करत आहे."</string>
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> तयार करत आहे."</string>
- <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"अॅप्स प्रारंभ करत आहे."</string>
+ <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"अॅप्स सुरू करत आहे."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"बूट समाप्त होत आहे."</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"रन होणारे <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"गेमवर परत जाण्यासाठी टॅप करा"</string>
@@ -1288,7 +1293,7 @@
<string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"नेहमी अनुमती द्या"</string>
<string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"कधीही अनुमती देऊ नका"</string>
<string name="sim_removed_title" msgid="5387212933992546283">"सिम कार्ड काढले"</string>
- <string name="sim_removed_message" msgid="9051174064474904617">"तुम्ही एक वैध सिम कार्ड घालून प्रारंभ करेपर्यंत मोबाइल नेटवर्क अनुपलब्ध असेल."</string>
+ <string name="sim_removed_message" msgid="9051174064474904617">"तुम्ही एक वैध सिम कार्ड घालून सुरू करेपर्यंत मोबाइल नेटवर्क अनुपलब्ध असेल."</string>
<string name="sim_done_button" msgid="6464250841528410598">"पूर्ण झाले"</string>
<string name="sim_added_title" msgid="7930779986759414595">"सिम कार्ड जोडले"</string>
<string name="sim_added_message" msgid="6602906609509958680">"मोबाइल नेटवर्कवर अॅक्सेस करण्यासाठी तुमचे डिव्हाइस रीस्टार्ट करा."</string>
@@ -1917,7 +1922,7 @@
<string name="unpin_specific_target" msgid="3859828252160908146">"<xliff:g id="LABEL">%1$s</xliff:g> ला अनपिन करा"</string>
<string name="app_info" msgid="6113278084877079851">"अॅप माहिती"</string>
<string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="demo_starting_message" msgid="6577581216125805905">"डेमो प्रारंभ करत आहे..."</string>
+ <string name="demo_starting_message" msgid="6577581216125805905">"डेमो सुरू करत आहे..."</string>
<string name="demo_restarting_message" msgid="1160053183701746766">"डिव्हाइस रीसेट करत आहे..."</string>
<string name="suspended_widget_accessibility" msgid="6331451091851326101">"<xliff:g id="LABEL">%1$s</xliff:g> अक्षम केले"</string>
<string name="conference_call" msgid="5731633152336490471">"कॉंफरन्स कॉल"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index f45f32e..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>
@@ -146,6 +147,10 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Panggil melalui Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Panggil melalui rangkaian mudah alih"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi sahaja"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <skip />
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Tidak dimajukan"</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> selepas <xliff:g id="TIME_DELAY">{2}</xliff:g> saat"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 0cc4d8e..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Wi-Fi သုံး၍ ခေါ်ဆိုသည်"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"မိုဘိုင်းကွန်ရက်သုံး၍ ခေါ်ဆိုသည်"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"ကြိုးမဲ့အင်တာနက် သာလျှင်"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 601c6fb..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Ring via Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Ring over mobilnettverk"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Bare Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 e53a519..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Wi-Fi मार्फत कल गर्नुहोस्"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"मोबाइल नेटवर्कमार्फत कल गर्नुहोस्"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi मात्र"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 37eb025..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Bellen via wifi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Bellen via mobiel netwerk"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Alleen wifi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 2124d49..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>
@@ -146,6 +147,10 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"ୱାଇ-ଫାଇ ମାଧ୍ୟମରେ କଲ୍ କରନ୍ତୁ"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"ମୋବାଇଲ ନେଟ୍ୱର୍କ ମାଧ୍ୟମରେ କଲ୍ କରନ୍ତୁ"</string>
<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="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>
@@ -1124,9 +1129,9 @@
<string name="app_running_notification_title" msgid="8985999749231486569">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଚାଲୁଛି"</string>
<string name="app_running_notification_text" msgid="5120815883400228566">"ଅଧିକ ସୂଚନା ପାଇଁ କିମ୍ବା ଆପ୍ ବନ୍ଦ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string>
<string name="ok" msgid="2646370155170753815">"ଠିକ୍ ଅଛି"</string>
- <string name="cancel" msgid="6908697720451760115">"କ୍ୟାନ୍ସଲ୍ କରନ୍ତୁ"</string>
+ <string name="cancel" msgid="6908697720451760115">"ବାତିଲ୍ କରନ୍ତୁ"</string>
<string name="yes" msgid="9069828999585032361">"ଠିକ୍ ଅଛି"</string>
- <string name="no" msgid="5122037903299899715">"କ୍ୟାନ୍ସଲ୍ କରନ୍ତୁ"</string>
+ <string name="no" msgid="5122037903299899715">"ବାତିଲ୍ କରନ୍ତୁ"</string>
<string name="dialog_alert_title" msgid="651856561974090712">"ଧ୍ୟାନଦିଅନ୍ତୁ"</string>
<string name="loading" msgid="3138021523725055037">"ଲୋଡ୍ କରାଯାଉଛି…"</string>
<string name="capital_on" msgid="2770685323900821829">"ଚାଲୁ"</string>
@@ -1282,7 +1287,7 @@
<string name="sms_short_code_details" msgid="2723725738333388351">"ଏହା ଦ୍ୱାରା "<b>" ଆପଣଙ୍କ ମୋବାଇଲ୍ ଆକାଉଣ୍ଟରୁ ପଇସା କଟିପାରେ। "</b></string>
<string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>" ଆପଣଙ୍କ ମୋବାଇଲ୍ ଆକାଉଣ୍ଟରୁ ପଇସା କଟିପାରେ। "</b></string>
<string name="sms_short_code_confirm_allow" msgid="920477594325526691">"ପଠାନ୍ତୁ"</string>
- <string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"କ୍ୟାନ୍ସଲ୍ କରନ୍ତୁ"</string>
+ <string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"ବାତିଲ୍ କରନ୍ତୁ"</string>
<string name="sms_short_code_remember_choice" msgid="1374526438647744862">"ମୋ ପସନ୍ଦ ମନେରଖନ୍ତୁ"</string>
<string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"ଏହାକୁ ଆପଣ ସେଟିଙ୍ଗ ଓ ଆପ୍ରେ ପରବର୍ତ୍ତୀ ସମୟରେ ବଦଳାଇପାରିବେ"</string>
<string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"ସର୍ବଦା ଅନୁମତି ଦିଅନ୍ତୁ"</string>
@@ -1500,7 +1505,7 @@
<string name="date_picker_prev_month_button" msgid="3418694374017868369">"ପୂର୍ବ ମାସ"</string>
<string name="date_picker_next_month_button" msgid="4858207337779144840">"ପରବର୍ତ୍ତୀ ମାସ"</string>
<string name="keyboardview_keycode_alt" msgid="8997420058584292385">"ALT"</string>
- <string name="keyboardview_keycode_cancel" msgid="2134624484115716975">"କ୍ୟାନ୍ସଲ୍ କରନ୍ତୁ"</string>
+ <string name="keyboardview_keycode_cancel" msgid="2134624484115716975">"ବାତିଲ୍ କରନ୍ତୁ"</string>
<string name="keyboardview_keycode_delete" msgid="2661117313730098650">"ଡିଲିଟ୍ କରନ୍ତୁ"</string>
<string name="keyboardview_keycode_done" msgid="2524518019001653851">"ହୋଇଗଲା"</string>
<string name="keyboardview_keycode_mode_change" msgid="2743735349997999020">"ମୋଡ୍ ପରିବର୍ତ୍ତନ"</string>
@@ -1761,7 +1766,7 @@
<string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string>
<string name="mediasize_unknown_portrait" msgid="3817016220446495613">"ଅଜଣା ପୋର୍ଟ୍ରେଟ୍"</string>
<string name="mediasize_unknown_landscape" msgid="1584741567225095325">"ଅଜଣା ଲ୍ୟାଣ୍ଡସ୍କେପ୍"</string>
- <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"କ୍ୟାନ୍ସଲ୍ କରାଗଲା"</string>
+ <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"ବାତିଲ୍ କରାଗଲା"</string>
<string name="write_fail_reason_cannot_write" msgid="432118118378451508">"କଣ୍ଟେଣ୍ଟ ଲେଖିବାବେଳେ ତ୍ରୁଟି"</string>
<string name="reason_unknown" msgid="5599739807581133337">"ଅଜଣା"</string>
<string name="reason_service_unavailable" msgid="5288405248063804713">"ପ୍ରିଣ୍ଟ ସେବାକୁ ସକ୍ଷମ କରାଯାଇନାହିଁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 4518a3b..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>
@@ -146,6 +147,10 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"ਵਾਈ-ਫਾਈ \'ਤੇ ਕਾਲ ਕਰੋ"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"ਮੋਬਾਈਲ ਨੈੱਟਵਰਕ ਤੋਂ ਕਾਲ ਕਰੋ"</string>
<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="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-pl/strings.xml b/core/res/res/values-pl/strings.xml
index cfcc2d5..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>
@@ -148,6 +149,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Rozmowa przez Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Połączenia przez sieć komórkową"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Tylko Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 e4092fd..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Chamar via Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Chamar via rede móvel"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Somente Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 d5c3c02..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Chamada por Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Chamada por rede móvel"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Apenas Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Chamadas com vários cartões SIM"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não reencaminhado"</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/strings.xml b/core/res/res/values-pt/strings.xml
index e4092fd..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Chamar via Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Chamar via rede móvel"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Somente Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 b2d2d24..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>
@@ -147,6 +148,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Apelați prin Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Apelați prin rețeaua mobilă"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Numai Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 a4d249a..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>
@@ -148,6 +149,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Звонить по Wi‑Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Звонить по мобильной сети"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Только Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 4277a0d..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Wi-Fi ඔස්සේ ඇමතුම"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"ජංගම ජාලය ඔස්සේ ඇමතුම"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi පමණයි"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 5f6d557..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>
@@ -148,6 +149,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Volanie cez Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Volanie cez mobilnú sieť"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Len Wi‑Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 0d77c61..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>
@@ -148,6 +149,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Klic prek omrežja Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Klic prek mobilnega omrežja"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 218412b..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>
@@ -146,6 +147,10 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Telefono nëpërmjet Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Telefono nëpërmjet rrjetit celular"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Vetëm Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <skip />
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (779976494687695991) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nuk u transferua"</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> pas <xliff:g id="TIME_DELAY">{2}</xliff:g> sekondash"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index e944e8b..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>
@@ -147,6 +148,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Позивање преко WiFi-а"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Позив преко мобилне мреже"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Само WiFi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="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 2acc051..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Ring via Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Ring via mobilnätverk"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Endast Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 079d71c..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Piga simu ukitumia WI-FI"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Piga ukitumia mtandao wa simu"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi pekee"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 ec9c0a7..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"வைஃபை மூலம் அழை"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"மொபைல் நெட்வொர்க் மூலமாக அழை"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"வைஃபை மட்டும்"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 6483db1..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Wi-Fi ద్వారా కాల్"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"మొబైల్ నెట్వర్క్ ద్వారా కాల్"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi మాత్రమే"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 094b6c8..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"โทรผ่าน Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"โทรผ่านเครือข่ายมือถือ"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi เท่านั้น"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 09ace93..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Tumawag gamit ang Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Tumawag gamit ang mobile network"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi lang"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 af10694..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Kablosuz ağ üzerinden arama"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Mobil ağ üzerinden arama"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Yalnızca kablosuz"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 f264fbb..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>
@@ -148,6 +149,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Телефонувати через Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Телефонувати через мобільну мережу"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Лише Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 37cb12a..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>
@@ -146,6 +147,10 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Wi-Fi پر کال کریں"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"موبائل نیٹ ورک پر کال کریں"</string>
<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="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-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 23fd462..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Wi-Fi orqali chaqiruv"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Mobil tarmoq orqali chaqiruv"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Faqat Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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>
@@ -1278,15 +1282,15 @@
<string name="sms_control_message" msgid="6574313876316388239">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> katta miqdordagi SMS xabarlarini jo‘natmoqda. Ushbu ilovaga xabarlar jo‘natishni davom ettirishga ruxsat berasizmi?"</string>
<string name="sms_control_yes" msgid="4858845109269524622">"Ruxsat berish"</string>
<string name="sms_control_no" msgid="4845717880040355570">"Rad etish"</string>
- <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>ga xabar jo‘natishni xohlaydi."</string>
- <string name="sms_short_code_details" msgid="2723725738333388351">"Bunda, mobil hisobingizdan "<b>"to‘lov olinishi mumkin"</b>"."</string>
- <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"Bunda, mobil hisobingizdan to‘lov olinishi mumkin."</b></string>
+ <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ilovasi <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b> raqamiga xabar yubormoqchi."</string>
+ <string name="sms_short_code_details" msgid="2723725738333388351">"Mobil aloqa hisobingizdan "<b>"pul olinishi mumkin"</b>"."</string>
+ <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"Mobil aloqa hisobingizdan pul olinishi mumkin."</b></string>
<string name="sms_short_code_confirm_allow" msgid="920477594325526691">"Yuborish"</string>
<string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"Bekor qilish"</string>
<string name="sms_short_code_remember_choice" msgid="1374526438647744862">"Tanlovim eslab qolinsin"</string>
- <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"Siz buni keyinroq sozlamalar > ilovalar menusidan o‘zgartirishingiz mumkin"</string>
- <string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"Doimo ruxsat berilsin"</string>
- <string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Hech qachon ruxsat berilmasin"</string>
+ <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"Buni keyinroq Sozlamalar > Ilovalar menyusidan o‘zgartirishingiz mumkin"</string>
+ <string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"Doim ruxsat"</string>
+ <string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Ruxsat berilmasin"</string>
<string name="sim_removed_title" msgid="5387212933992546283">"SIM karta olib tashlandi"</string>
<string name="sim_removed_message" msgid="9051174064474904617">"Ishlaydigan SIM kartani qo‘yib, qurilmangizni qaytadan ishga tushirmasangiz, mobayl tarmoq mavjud bo‘lmaydi."</string>
<string name="sim_done_button" msgid="6464250841528410598">"Tayyor"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index bfc509a..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Gọi qua Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Gọi qua mạng di động"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Chỉ Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 85a7dae..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"通过 WLAN 进行通话"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"通过移动网络进行通话"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"仅限 WLAN"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 6d06e68..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"使用 Wi-Fi 通話"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"使用流動網絡通話"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"只限 Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 5ba35c7..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"透過 Wi-Fi 進行通話"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"透過行動網路進行通話"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"只限 Wi-Fi"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <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 b391b4d..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>
@@ -146,6 +147,9 @@
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Ikholi esebenza nge-Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Shaya ngenethiwekhi yeselula"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"I-Wi-Fi kuphela"</string>
+ <!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
+ <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g>Ukwenza ikholi kwe-Cross Sim"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Akudlulisiwe"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> emuva kwamasekhondi angu-<xliff:g id="TIME_DELAY">{2}</xliff:g>"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index ef54db1a..be7ecfc 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9013,6 +9013,11 @@
changed at runtime by calling
{@link android.media.tv.TvInputManager#updateTvInputInfo(android.media.tv.TvInputInfo)}. -->
<attr name="tunerCount" format="integer" />
+ <!-- Attribute whether the TV input service can pause recording programs.
+ This value can be changed at runtime by calling
+ {@link android.media.tv.TvInputManager#updateTvInputInfo(android.media.tv.TvInputInfo)}
+ . -->
+ <attr name="canPauseRecording" format="boolean" />
</declare-styleable>
<!-- Attributes that can be used with <code>rating-system-definition</code> tags inside of the
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 060c8b1..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>
@@ -2494,7 +2495,11 @@
<code>grant-uri-permission</code> tag, a child of the
{@link #AndroidManifestProvider provider} tag, describing a specific
URI path that can be granted as a permission. This tag can be
- specified multiple time to supply multiple paths. -->
+ specified multiple time to supply multiple paths. If multiple
+ path matching attributes are supplied, they will be evaluated in the
+ following order with the first attribute being the only one honored:
+ <code>pathAdvancedPattern</code>, <code>pathPattern</code>,
+ <code>pathPrefix</code>, <code>pathSuffix</code>, <code>path</code>. -->
<declare-styleable name="AndroidManifestGrantUriPermission" parent="AndroidManifestProvider">
<!-- Specify a URI path that must exactly match, as per
{@link android.os.PatternMatcher} with
@@ -2514,18 +2519,37 @@
"\\\\". This is basically the same as what you would need to
write if constructing the string in Java code. -->
<attr name="pathPattern" format="string" />
+ <!-- Specify a URI path that matches an advanced pattern, as per
+ {@link android.os.PatternMatcher} with
+ {@link android.os.PatternMatcher#PATTERN_ADVANCED_GLOB}.
+ Note that because '\' is used as an escape character when
+ reading the string from XML (before it is parsed as a pattern),
+ you will need to double-escape: for example a literal "*" would
+ be written as "\\*" and a literal "\" would be written as
+ "\\\\". This is basically the same as what you would need to
+ write if constructing the string in Java code. -->
+ <attr name="pathAdvancedPattern" format="string"/>
+ <!-- Specify a URI path that must be a suffix to match, as per
+ {@link android.os.PatternMatcher} with
+ {@link android.os.PatternMatcher#PATTERN_SUFFIX}. -->
+ <attr name="pathSuffix" format="string" />
</declare-styleable>
<!-- Attributes that can be supplied in an AndroidManifest.xml
<code>path-permission</code> tag, a child of the
{@link #AndroidManifestProvider provider} tag, describing a permission
that allows access to a specific path in the provider. This tag can be
- specified multiple time to supply multiple paths. -->
+ specified multiple time to supply multiple paths. If multiple
+ path matching attributes are supplied, they will be evaluated in the
+ following order with the first attribute being the only one honored:
+ <code>pathAdvancedPattern</code>, <code>pathPattern</code>,
+ <code>pathPrefix</code>, <code>pathSuffix</code>, <code>path</code>.-->
<declare-styleable name="AndroidManifestPathPermission" parent="AndroidManifestProvider">
<attr name="path" />
<attr name="pathPrefix" />
<attr name="pathPattern" />
<attr name="pathAdvancedPattern" format="string"/>
+ <attr name="pathSuffix" />
<attr name="permission" />
<attr name="readPermission" />
<attr name="writePermission" />
@@ -2579,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.
@@ -2977,6 +3002,22 @@
"\\\\". This is basically the same as what you would need to
write if constructing the string in Java code. -->
<attr name="sspPattern" format="string" />
+ <!-- Specify a URI scheme specific part that matches an advanced pattern, as per
+ {@link android.content.IntentFilter#addDataSchemeSpecificPart
+ IntentFilter.addDataSchemeSpecificPart()} with
+ {@link android.os.PatternMatcher#PATTERN_ADVANCED_GLOB}.
+ Note that because '\' is used as an escape character when
+ reading the string from XML (before it is parsed as a pattern),
+ you will need to double-escape: for example a literal "*" would
+ be written as "\\*" and a literal "\" would be written as
+ "\\\\". This is basically the same as what you would need to
+ write if constructing the string in Java code. -->
+ <attr name="sspAdvancedPattern" format="string" />
+ <!-- Specify a URI scheme specific part that must be a suffix to match, as per
+ {@link android.content.IntentFilter#addDataSchemeSpecificPart
+ IntentFilter.addDataSchemeSpecificPart()} with
+ {@link android.os.PatternMatcher#PATTERN_SUFFIX}. -->
+ <attr name="sspSuffix" format="string" />
<!-- Specify a URI authority host that is handled, as per
{@link android.content.IntentFilter#addDataAuthority
IntentFilter.addDataAuthority()}.
@@ -3021,6 +3062,11 @@
"\\\\". This is basically the same as what you would need to
write if constructing the string in Java code. -->
<attr name="pathAdvancedPattern" />
+ <!-- Specify a URI path that must be a suffix to match, as per
+ {@link android.content.IntentFilter#addDataPath
+ IntentFilter.addDataPath()} with
+ {@link android.os.PatternMatcher#PATTERN_SUFFIX}. -->
+ <attr name="pathSuffix" />
</declare-styleable>
<!-- Attributes that can be supplied in an AndroidManifest.xml
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 110e77a..8940776 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -234,4 +234,78 @@
<color name="personal_apps_suspension_notification_color">#1A73E8</color>
<color name="conversation_important_highlight">#F9AB00</color>
+
+ <!-- Lightest shade of the main color used by the system. White.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_main_0">#ffffff</color>
+ <!-- Shade of the main system color at 95% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_main_50">#ebf1f8</color>
+ <!-- Shade of the main system color at 90% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_main_100">#dde3ea</color>
+ <!-- Shade of the main system color at 80% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_main_200">#c1c7cd</color>
+ <!-- Shade of the main system color at 70% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_main_300">#a6acb2</color>
+ <!-- Shade of the main system color at 60% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_main_400">#8b9197</color>
+ <!-- Shade of the main system color at 50% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_main_500">#72787d</color>
+ <!-- Shade of the main system color at 40% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_main_600">#595f64</color>
+ <!-- Shade of the main system color at 30% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_main_700">#42474d</color>
+ <!-- Shade of the main system color at 20% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_main_800">#2c3136</color>
+ <!-- Shade of the main system color at 10% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_main_900">#171c21</color>
+ <!-- Darkest shade of the main color used by the system. Black.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_main_1000">#000000</color>
+
+ <!-- Lightest shade of the accent color used by the system. White.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent_0">#ffffff</color>
+ <!-- Shade of the accent system color at 95% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent_50">#91fff4</color>
+ <!-- Shade of the accent system color at 90% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent_100">#83f6e5</color>
+ <!-- Shade of the accent system color at 80% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent_200">#65d9c9</color>
+ <!-- Shade of the accent system color at 70% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent_300">#45bdae</color>
+ <!-- Shade of the accent system color at 60% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent_400">#1fa293</color>
+ <!-- Shade of the accent system color at 50% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent_500">#00877a</color>
+ <!-- Shade of the accent system color at 40% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent_600">#006d61</color>
+ <!-- Shade of the accent system color at 30% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent_700">#005449</color>
+ <!-- Shade of the accent system color at 20% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent_800">#003c33</color>
+ <!-- Shade of the accent system color at 10% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent_900">#00271e</color>
+ <!-- Darkest shade of the accent color used by the system. Black.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent_1000">#000000</color>
</resources>
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index 310ca89..b558087 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -33,14 +33,14 @@
<color name="tertiary_device_default_settings">@color/tertiary_material_settings</color>
<color name="quaternary_device_default_settings">@color/quaternary_material_settings</color>
- <color name="accent_device_default_light">@color/accent_material_light</color>
- <color name="accent_device_default_dark">@color/accent_material_dark</color>
+ <color name="accent_device_default_light">@color/system_accent_500</color>
+ <color name="accent_device_default_dark">@color/system_accent_200</color>
<color name="accent_device_default">@color/accent_device_default_light</color>
- <color name="background_device_default_dark">#1A1A1A</color>
- <color name="background_device_default_light">#F2F2F2</color>
- <color name="background_floating_device_default_dark">#0D0D0D</color>
- <color name="background_floating_device_default_light">#CCCCCC</color>
+ <color name="background_device_default_dark">@color/system_main_900</color>
+ <color name="background_device_default_light">@color/system_main_100</color>
+ <color name="background_floating_device_default_dark">@color/system_main_800</color>
+ <color name="background_floating_device_default_light">@color/system_main_200</color>
<!-- Error color -->
<color name="error_color_device_default_dark">@color/error_color_material_dark</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d8da600..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>
@@ -4605,4 +4612,14 @@
happens that doesn't result in bringing the home task to the front.
This is currently only used on TV. -->
<bool name="config_enableBackSound">false</bool>
+
+ <!-- Chooser image editing activity. Must handle ACTION_EDIT image/png intents.
+ 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/ids.xml b/core/res/res/values/ids.xml
index a12d2a9..a4c7293 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -206,6 +206,9 @@
<!-- Marks the "nearby" button in the ChooserActivity -->
<item type="id" name="chooser_nearby_button" />
+ <!-- Marks the "edit" button in the ChooserActivity -->
+ <item type="id" name="chooser_edit_button" />
+
<!-- Accessibility action identifier for {@link android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_BACK}. -->
<item type="id" name="accessibilitySystemActionBack" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 0958434..bac50f1 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3047,9 +3047,14 @@
<public name="rollbackDataPolicy" />
<public name="allowClickWhenDisabled" />
<public name="windowLayoutAffinity" />
+ <public name="canPauseRecording" />
<!-- @hide -->
<public name="windowBackgroundBlurRadius"/>
<public name="requireDeviceScreenOn" />
+ <public name="pathSuffix" />
+ <public name="sspSuffix" />
+ <public name="pathAdvancedPattern" />
+ <public name="sspAdvancedPattern" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
@@ -3058,6 +3063,34 @@
<public-group type="color" first-id="0x0106001d">
<!-- color definitions go here -->
+
+ <!-- Material design dynamic system palette:-->
+ <!-- Dominant color -->
+ <public name="system_main_0" />
+ <public name="system_main_50" />
+ <public name="system_main_100" />
+ <public name="system_main_200" />
+ <public name="system_main_300" />
+ <public name="system_main_400" />
+ <public name="system_main_500" />
+ <public name="system_main_600" />
+ <public name="system_main_700" />
+ <public name="system_main_800" />
+ <public name="system_main_900" />
+ <public name="system_main_1000" />
+ <!-- Accent color -->
+ <public name="system_accent_0" />
+ <public name="system_accent_50" />
+ <public name="system_accent_100" />
+ <public name="system_accent_200" />
+ <public name="system_accent_300" />
+ <public name="system_accent_400" />
+ <public name="system_accent_500" />
+ <public name="system_accent_600" />
+ <public name="system_accent_700" />
+ <public name="system_accent_800" />
+ <public name="system_accent_900" />
+ <public name="system_accent_1000" />
</public-group>
<public-group type="dimen" first-id="0x01050008">
diff --git a/core/res/res/values/required_apps_managed_device.xml b/core/res/res/values/required_apps_managed_device.xml
index e17d214..c455bd8 100644
--- a/core/res/res/values/required_apps_managed_device.xml
+++ b/core/res/res/values/required_apps_managed_device.xml
@@ -28,5 +28,6 @@
<item>com.android.providers.downloads</item>
<item>com.android.providers.downloads.ui</item>
<item>com.android.documentsui</item>
+ <item>com.android.cellbroadcastreceiver</item>
</string-array>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 5715b31..317a76f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -110,7 +110,7 @@
<!-- Displayed as the title for a success/failure report enabling/disabling caller ID. -->
<string name="ClipMmi">Incoming Caller ID</string>
<!-- Displayed as the title for a success/failure report enabling/disabling caller ID. -->
- <string name="ClirMmi">Outgoing Caller ID</string>
+ <string name="ClirMmi">Hide Outgoing Caller ID</string>
<!-- Displayed as the title for a success/failure report enabling/disabling connected line ID. -->
<string name="ColpMmi">Connected Line ID</string>
<!-- Displayed as the title for a success/failure report enabling/disabling connected line ID restriction. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 236f7cb..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" />
@@ -2045,6 +2046,7 @@
<java-symbol type="string" name="car_mode_disable_notification_message" />
<java-symbol type="string" name="car_mode_disable_notification_title" />
<java-symbol type="string" name="chooser_wallpaper" />
+ <java-symbol type="string" name="config_systemImageEditor" />
<java-symbol type="string" name="config_datause_iface" />
<java-symbol type="string" name="config_activityRecognitionHardwarePackageName" />
<java-symbol type="string" name="config_fusedLocationProviderPackageName" />
@@ -2946,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" />
@@ -3895,6 +3898,7 @@
<java-symbol type="drawable" name="chooser_dialog_background" />
<java-symbol type="id" name="chooser_copy_button" />
<java-symbol type="id" name="chooser_nearby_button" />
+ <java-symbol type="id" name="chooser_edit_button" />
<java-symbol type="layout" name="chooser_action_button" />
<java-symbol type="dimen" name="chooser_action_button_icon_size" />
<java-symbol type="string" name="config_defaultNearbySharingComponent" />
@@ -4144,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/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
index a5ef2b4..d8ed805 100644
--- a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
+++ b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
@@ -23,7 +23,10 @@
import static org.junit.Assert.fail;
import android.Manifest;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.os.BugreportManager;
import android.os.BugreportManager.BugreportCallback;
import android.os.BugreportParams;
@@ -31,7 +34,9 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.StrictMode;
+import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -53,10 +58,11 @@
import org.junit.runners.JUnit4;
import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
-
/**
* Tests for BugreportManager API.
*/
@@ -67,8 +73,16 @@
private static final String TAG = "BugreportManagerTest";
private static final long BUGREPORT_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(10);
+ private static final long DUMPSTATE_STARTUP_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10);
private static final long UIAUTOMATOR_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10);
+ // Sent by Shell when its bugreport finishes (contains final bugreport/screenshot file name
+ // associated with the bugreport).
+ private static final String INTENT_BUGREPORT_FINISHED =
+ "com.android.internal.intent.action.BUGREPORT_FINISHED";
+ private static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT";
+ private static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
+
private Handler mHandler;
private Executor mExecutor;
private BugreportManager mBrm;
@@ -212,6 +226,48 @@
}
@Test
+ public void cancelBugreport_noReportStarted() throws Exception {
+ // Without the native DumpstateService running, we don't get a SecurityException.
+ mBrm.cancelBugreport();
+ }
+
+ @LargeTest
+ @Test
+ public void cancelBugreport_fromDifferentUid() throws Exception {
+ assertThat(Process.myUid()).isNotEqualTo(Process.SHELL_UID);
+
+ // Start a bugreport through ActivityManager's shell command - this starts a BR from the
+ // shell UID rather than our own.
+ BugreportBroadcastReceiver br = new BugreportBroadcastReceiver();
+ InstrumentationRegistry.getContext()
+ .registerReceiver(br, new IntentFilter(INTENT_BUGREPORT_FINISHED));
+ UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ .executeShellCommand("am bug-report");
+
+ // The command triggers the report through a broadcast, so wait until dumpstate actually
+ // starts up, which may take a bit.
+ waitTillDumpstateRunningOrTimeout();
+
+ try {
+ mBrm.cancelBugreport();
+ fail("Expected cancelBugreport to throw SecurityException when report started by "
+ + "different UID");
+ } catch (SecurityException expected) {
+ } finally {
+ // Do this in the finally block so that even if this test case fails, we don't break
+ // other test cases unexpectedly due to the still-running shell report.
+ try {
+ // The shell's BR is still running and should complete successfully.
+ br.waitForBugreportFinished();
+ } finally {
+ // The latch may fail for a number of reasons but we still need to unregister the
+ // BroadcastReceiver.
+ InstrumentationRegistry.getContext().unregisterReceiver(br);
+ }
+ }
+ }
+
+ @Test
public void insufficientPermissions_throwsException() throws Exception {
dropPermissions();
@@ -347,6 +403,28 @@
.adoptShellPermissionIdentity(Manifest.permission.DUMP);
}
+ private static boolean isDumpstateRunning() {
+ String[] output;
+ try {
+ output =
+ UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ .executeShellCommand("ps -A -o NAME | grep dumpstate")
+ .trim()
+ .split("\n");
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to check if dumpstate is running", e);
+ return false;
+ }
+ for (String line : output) {
+ // Check for an exact match since there may be other things that contain "dumpstate" as
+ // a substring (e.g. the dumpstate HAL).
+ if (TextUtils.equals("dumpstate", line)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private static void assertFdIsClosed(ParcelFileDescriptor pfd) {
try {
int fd = pfd.getFd();
@@ -365,18 +443,25 @@
return System.currentTimeMillis();
}
- private static boolean shouldTimeout(long startTimeMs) {
- return now() - startTimeMs >= BUGREPORT_TIMEOUT_MS;
+ private static void waitTillDumpstateRunningOrTimeout() throws Exception {
+ long startTimeMs = now();
+ while (!isDumpstateRunning()) {
+ Thread.sleep(500 /* .5s */);
+ if (now() - startTimeMs >= DUMPSTATE_STARTUP_TIMEOUT_MS) {
+ break;
+ }
+ Log.d(TAG, "Waited " + (now() - startTimeMs) + "ms for dumpstate to start");
+ }
}
private static void waitTillDoneOrTimeout(BugreportCallbackImpl callback) throws Exception {
long startTimeMs = now();
while (!callback.isDone()) {
Thread.sleep(1000 /* 1s */);
- if (shouldTimeout(startTimeMs)) {
+ if (now() - startTimeMs >= BUGREPORT_TIMEOUT_MS) {
break;
}
- Log.d(TAG, "Waited " + (now() - startTimeMs) + "ms");
+ Log.d(TAG, "Waited " + (now() - startTimeMs) + "ms for bugreport to finish");
}
}
@@ -451,6 +536,36 @@
assertTrue(device.wait(Until.gone(consentTitleObj), UIAUTOMATOR_TIMEOUT_MS));
}
+ private class BugreportBroadcastReceiver extends BroadcastReceiver {
+ Intent mBugreportFinishedIntent = null;
+ final CountDownLatch mLatch;
+
+ BugreportBroadcastReceiver() {
+ mLatch = new CountDownLatch(1);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ setBugreportFinishedIntent(intent);
+ mLatch.countDown();
+ }
+
+ private void setBugreportFinishedIntent(Intent intent) {
+ mBugreportFinishedIntent = intent;
+ }
+
+ public Intent getBugreportFinishedIntent() {
+ return mBugreportFinishedIntent;
+ }
+
+ public void waitForBugreportFinished() throws Exception {
+ if (!mLatch.await(BUGREPORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ throw new Exception("Failed to receive BUGREPORT_FINISHED in "
+ + BUGREPORT_TIMEOUT_MS + " ms.");
+ }
+ }
+ }
+
/**
* A rule to change strict mode vm policy temporarily till test method finished.
*
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
new file mode 100644
index 0000000..986079f
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 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.app.appsearch;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Test;
+
+import java.util.Set;
+
+public class PutDocumentsRequestTest {
+
+ @Test
+ public void addGenericDocument_byCollection() {
+ Set<AppSearchEmail> emails =
+ ImmutableSet.of(
+ new AppSearchEmail.Builder("test1").build(),
+ new AppSearchEmail.Builder("test2").build());
+ PutDocumentsRequest request =
+ new PutDocumentsRequest.Builder().addGenericDocument(emails).build();
+
+ assertThat(request.getDocuments().get(0).getUri()).isEqualTo("test1");
+ assertThat(request.getDocuments().get(1).getUri()).isEqualTo("test2");
+ }
+}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
index b3caecc..c8cee85 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
@@ -16,6 +16,7 @@
package android.app.appsearch;
+
import static com.google.common.truth.Truth.assertThat;
import android.os.Bundle;
@@ -26,6 +27,7 @@
import java.util.Map;
public class SearchSpecTest {
+
@Test
public void testGetBundle() {
SearchSpec searchSpec =
@@ -33,6 +35,7 @@
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
.addNamespace("namespace1", "namespace2")
.addSchemaType("schemaTypes1", "schemaTypes2")
+ .addFilterPackageNames("package1", "package2")
.setSnippetCount(5)
.setSnippetCountPerProperty(10)
.setMaxSnippetSize(15)
@@ -48,6 +51,8 @@
.containsExactly("namespace1", "namespace2");
assertThat(bundle.getStringArrayList(SearchSpec.SCHEMA_TYPE_FIELD))
.containsExactly("schemaTypes1", "schemaTypes2");
+ assertThat(bundle.getStringArrayList(SearchSpec.PACKAGE_NAME_FIELD))
+ .containsExactly("package1", "package2");
assertThat(bundle.getInt(SearchSpec.SNIPPET_COUNT_FIELD)).isEqualTo(5);
assertThat(bundle.getInt(SearchSpec.SNIPPET_COUNT_PER_PROPERTY_FIELD)).isEqualTo(10);
assertThat(bundle.getInt(SearchSpec.MAX_SNIPPET_FIELD)).isEqualTo(15);
@@ -62,15 +67,26 @@
SearchSpec searchSpec =
new SearchSpec.Builder()
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
- .addProjectionTypePropertyPaths("TypeA", "field1", "field2.subfield2")
- .addProjectionTypePropertyPaths("TypeB", "field7")
- .addProjectionTypePropertyPaths("TypeC")
+ .addProjection("TypeA", "field1", "field2.subfield2")
+ .addProjection("TypeB", "field7")
+ .addProjection("TypeC")
.build();
- Map<String, List<String>> typePropertyPathMap = searchSpec.getProjectionTypePropertyPaths();
+ Map<String, List<String>> typePropertyPathMap = searchSpec.getProjections();
assertThat(typePropertyPathMap.keySet()).containsExactly("TypeA", "TypeB", "TypeC");
assertThat(typePropertyPathMap.get("TypeA")).containsExactly("field1", "field2.subfield2");
assertThat(typePropertyPathMap.get("TypeB")).containsExactly("field7");
assertThat(typePropertyPathMap.get("TypeC")).isEmpty();
}
+
+ @Test
+ public void testGetRankingStrategy() {
+ SearchSpec searchSpec =
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+ .setRankingStrategy(SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE)
+ .build();
+ assertThat(searchSpec.getRankingStrategy())
+ .isEqualTo(SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE);
+ }
}
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/autofill/OWNERS b/core/tests/coretests/src/android/view/autofill/OWNERS
new file mode 100644
index 0000000..9a30e82
--- /dev/null
+++ b/core/tests/coretests/src/android/view/autofill/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 351486
+
+include /core/java/android/view/autofill/OWNERS
diff --git a/core/tests/coretests/src/android/view/contentcapture/OWNERS b/core/tests/coretests/src/android/view/contentcapture/OWNERS
new file mode 100644
index 0000000..24561c5
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentcapture/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 544200
+
+include /core/java/android/view/contentcapture/OWNERS
diff --git a/core/tests/coretests/src/android/view/textclassifier/OWNERS b/core/tests/coretests/src/android/view/textclassifier/OWNERS
new file mode 100644
index 0000000..46b3cb8
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/service/textclassifier/OWNERS
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
index 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/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 787879a..d9012f64 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -638,6 +638,59 @@
}
+
+ @Test
+ public void testEditImageLogs() throws Exception {
+ Intent sendIntent = createSendImageIntent(
+ Uri.parse("android.resource://com.android.frameworks.coretests/"
+ + com.android.frameworks.coretests.R.drawable.test320x240));
+
+ sOverrides.previewThumbnail = createBitmap();
+ sOverrides.isImageType = true;
+
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+ final ChooserWrapperActivity activity = mActivityRule
+ .launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+
+ onView(withId(R.id.chooser_edit_button)).check(matches(isDisplayed()));
+ onView(withId(R.id.chooser_edit_button)).perform(click());
+
+ ChooserActivityLoggerFake logger =
+ (ChooserActivityLoggerFake) activity.getChooserActivityLogger();
+ // first one should be SHARESHEET_TRIGGERED uievent
+ assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
+ assertThat(logger.get(0).event.getId(),
+ is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
+ // second one should be SHARESHEET_STARTED event
+ assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
+ assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
+ assertThat(logger.get(1).mimeType, is("image/png"));
+ assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+ assertThat(logger.get(1).appProvidedApp, is(0));
+ assertThat(logger.get(1).appProvidedDirect, is(0));
+ assertThat(logger.get(1).isWorkprofile, is(false));
+ assertThat(logger.get(1).previewType, is(1));
+ // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
+ assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
+ assertThat(logger.get(2).event.getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
+ // fourth and fifth are just artifacts of test set-up
+ // sixth one should be ranking atom with SHARESHEET_EDIT_TARGET_SELECTED event
+ assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
+ assertThat(logger.get(5).targetType,
+ is(ChooserActivityLogger
+ .SharesheetTargetSelectedEvent.SHARESHEET_EDIT_TARGET_SELECTED.getId()));
+ }
+
+
@Test
public void oneVisibleImagePreview() throws InterruptedException {
Uri uri = Uri.parse("android.resource://com.android.frameworks.coretests/"
@@ -2202,6 +2255,19 @@
return sendIntent;
}
+ private Intent createSendImageIntent(Uri imageThumbnail) {
+ Intent sendIntent = new Intent();
+ sendIntent.setAction(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_STREAM, imageThumbnail);
+ sendIntent.setType("image/png");
+ if (imageThumbnail != null) {
+ ClipData.Item clipItem = new ClipData.Item(imageThumbnail);
+ sendIntent.setClipData(new ClipData("Clip Label", new String[]{"image/png"}, clipItem));
+ }
+
+ return sendIntent;
+ }
+
private Intent createSendTextIntentWithPreview(String title, Uri imageThumbnail) {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
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/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index 9531181d..6019b90 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -395,6 +395,16 @@
}
@Override
+ public void addCecSettingChangeListener(String name,
+ IHdmiCecSettingChangeListener listener) {
+ }
+
+ @Override
+ public void removeCecSettingChangeListener(String name,
+ IHdmiCecSettingChangeListener listener) {
+ }
+
+ @Override
public int[] getAllowedCecSettingIntValues(String name) {
return new int[0];
}
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 49888fd..8dd7f31 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -71,8 +71,8 @@
}
/*package*/
- long finishRecording() {
- return nFinishRecording(mNativeCanvasWrapper);
+ void finishRecording(RenderNode node) {
+ nFinishRecording(mNativeCanvasWrapper, node.mNativeRenderNode);
}
///////////////////////////////////////////////////////////////////////////
@@ -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());
}
/**
@@ -271,7 +271,7 @@
@CriticalNative
private static native void nEnableZ(long renderer, boolean enableZ);
@CriticalNative
- private static native long nFinishRecording(long renderer);
+ private static native void nFinishRecording(long renderer, long renderNode);
@CriticalNative
private static native void nDrawRenderNode(long renderer, long renderNode);
@CriticalNative
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 117828d..c1310a9 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -30,7 +30,6 @@
import com.android.internal.util.ArrayUtils;
import dalvik.annotation.optimization.CriticalNative;
-import dalvik.annotation.optimization.FastNative;
import libcore.util.NativeAllocationRegistry;
@@ -406,8 +405,7 @@
}
RecordingCanvas canvas = mCurrentRecordingCanvas;
mCurrentRecordingCanvas = null;
- long displayList = canvas.finishRecording();
- nSetDisplayList(mNativeRenderNode, displayList);
+ canvas.finishRecording(this);
canvas.recycle();
}
@@ -438,7 +436,7 @@
* obsolete resources after related resources are gone.
*/
public void discardDisplayList() {
- nSetDisplayList(mNativeRenderNode, 0);
+ nDiscardDisplayList(mNativeRenderNode);
}
/**
@@ -1528,20 +1526,14 @@
private static native void nEndAllAnimators(long renderNode);
-
- ///////////////////////////////////////////////////////////////////////////
- // @FastNative methods
- ///////////////////////////////////////////////////////////////////////////
-
- @FastNative
- private static native void nSetDisplayList(long renderNode, long newData);
-
-
///////////////////////////////////////////////////////////////////////////
// @CriticalNative methods
///////////////////////////////////////////////////////////////////////////
@CriticalNative
+ private static native void nDiscardDisplayList(long renderNode);
+
+ @CriticalNative
private static native boolean nIsValid(long renderNode);
// Matrix
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/FontFileUtil.java b/graphics/java/android/graphics/fonts/FontFileUtil.java
index f8b456b..2896c46 100644
--- a/graphics/java/android/graphics/fonts/FontFileUtil.java
+++ b/graphics/java/android/graphics/fonts/FontFileUtil.java
@@ -20,6 +20,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import dalvik.annotation.optimization.FastNative;
+
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -131,4 +133,44 @@
buffer.order(originalOrder);
}
}
+
+ /**
+ * Analyze head OpenType table and return fontRevision value as 32bit integer.
+ *
+ * The font revision is stored in 16.16 bit fixed point value. This function returns this fixed
+ * point value as 32 bit integer, i.e. the value multiplied with 65536.
+ *
+ * IllegalArgumentException will be thrown for invalid font data.
+ * If the font file is invalid, returns -1L.
+ *
+ * @param buffer a buffer of OpenType font
+ * @param index a font index
+ * @return font revision that shifted 16 bits left.
+ */
+ public static long getRevision(@NonNull ByteBuffer buffer, @IntRange(from = 0) int index) {
+ return nGetFontRevision(buffer, index);
+ }
+
+ /**
+ * Analyze name OpenType table and return PostScript name.
+ *
+ * IllegalArgumentException will be thrown for invalid font data.
+ * null will be returned if not found or the PostScript name is invalid.
+ *
+ * @param buffer a buffer of OpenType font
+ * @param index a font index
+ * @return a post script name or null if it is invalid or not found.
+ */
+ public static String getPostScriptName(@NonNull ByteBuffer buffer,
+ @IntRange(from = 0) int index) {
+ return nGetFontPostScriptName(buffer, index);
+ }
+
+ @FastNative
+ private static native long nGetFontRevision(@NonNull ByteBuffer buffer,
+ @IntRange(from = 0) int index);
+
+ @FastNative
+ private static native String nGetFontPostScriptName(@NonNull ByteBuffer buffer,
+ @IntRange(from = 0) int index);
}
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/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/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index 707747b..e17a943 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.util.AttributeSet;
+import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.SurfaceControlViewHost;
import android.view.VelocityTracker;
@@ -54,6 +55,7 @@
private VelocityTracker mVelocityTracker;
private boolean mMoving;
private int mStartPos;
+ private GestureDetector mDoubleTapDetector;
public DividerView(@NonNull Context context) {
super(context);
@@ -88,6 +90,7 @@
mBackground = findViewById(R.id.docked_divider_background);
mTouchElevation = getResources().getDimensionPixelSize(
R.dimen.docked_stack_divider_lift_elevation);
+ mDoubleTapDetector = new GestureDetector(getContext(), new DoubleTapListener());
setOnTouchListener(this);
}
@@ -136,6 +139,8 @@
mSplitLayout.snapToTarget(position, snapTarget);
break;
}
+
+ mDoubleTapDetector.onTouchEvent(event);
return true;
}
@@ -200,4 +205,14 @@
private boolean isLandscape() {
return getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
}
+
+ private class DoubleTapListener extends GestureDetector.SimpleOnGestureListener {
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ if (mSplitLayout != null) {
+ mSplitLayout.onDoubleTappedDivider();
+ }
+ return false;
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 9721d30..d77def5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -201,6 +201,10 @@
}
}
+ void onDoubleTappedDivider() {
+ mLayoutChangeListener.onDoubleTappedDivider();
+ }
+
/**
* Returns {@link DividerSnapAlgorithm.SnapTarget} which matches passing position and velocity.
*/
@@ -249,9 +253,15 @@
public interface LayoutChangeListener {
/** Calls when dismissing split. */
void onSnappedToDismiss(boolean snappedToEnd);
+
/** Calls when the bounds is changing due to animation or dragging divider bar. */
void onBoundsChanging(SplitLayout layout);
+
/** Calls when the target bounds changed. */
void onBoundsChanged(SplitLayout layout);
+
+ /** Calls when user double tapped on the divider bar. */
+ default void onDoubleTappedDivider() {
+ }
}
}
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/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 0c6edf1..bdac37a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -271,6 +271,12 @@
}
@Override
+ public void onDoubleTappedDivider() {
+ setSideStagePosition(mSideStagePosition == SIDE_STAGE_POSITION_TOP_OR_LEFT
+ ? SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT : SIDE_STAGE_POSITION_TOP_OR_LEFT);
+ }
+
+ @Override
public void onBoundsChanged(SplitLayout layout) {
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
if (dividerLeash == null) return;
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/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 0d1c6f9..5821eed 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -83,6 +83,12 @@
}
@Test
+ public void testOnDoubleTappedDivider() {
+ mSplitLayout.onDoubleTappedDivider();
+ verify(mLayoutChangeListener).onDoubleTappedDivider();
+ }
+
+ @Test
@UiThreadTest
public void testSnapToDismissTarget() {
// verify it callbacks properly when the snap target indicates dismissing split.
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/DisplayList.h b/libs/hwui/DisplayList.h
index dd24763..3aa5b4b 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -16,21 +16,134 @@
#pragma once
+#include "pipeline/skia/SkiaDisplayList.h"
+
+#include <memory>
+
namespace android {
namespace uirenderer {
namespace VectorDrawable {
class Tree;
};
-namespace skiapipeline {
-class SkiaDisplayList;
-}
typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
/**
* Data structure that holds the list of commands used in display list stream
*/
-using DisplayList = skiapipeline::SkiaDisplayList;
+//using DisplayList = skiapipeline::SkiaDisplayList;
+class DisplayList {
+public:
+ // Constructs an empty (invalid) DisplayList
+ explicit DisplayList() {}
+
+ // Constructs a DisplayList from a SkiaDisplayList
+ explicit DisplayList(std::unique_ptr<skiapipeline::SkiaDisplayList> impl)
+ : mImpl(std::move(impl)) {}
+
+ // Move support
+ DisplayList(DisplayList&& other) : mImpl(std::move(other.mImpl)) {}
+ DisplayList& operator=(DisplayList&& other) {
+ mImpl = std::move(other.mImpl);
+ return *this;
+ }
+
+ // No copy support
+ DisplayList(const DisplayList& other) = delete;
+ DisplayList& operator=(const DisplayList&) = delete;
+
+ void updateChildren(std::function<void(RenderNode*)> updateFn) {
+ mImpl->updateChildren(std::move(updateFn));
+ }
+
+ [[nodiscard]] explicit operator bool() const {
+ return mImpl.get() != nullptr;
+ }
+
+ // If true this DisplayList contains a backing content, even if that content is empty
+ // If false, there this DisplayList is in an "empty" state
+ [[nodiscard]] bool isValid() const {
+ return mImpl.get() != nullptr;
+ }
+
+ [[nodiscard]] bool isEmpty() const {
+ return !hasContent();
+ }
+
+ [[nodiscard]] bool hasContent() const {
+ return mImpl && !(mImpl->isEmpty());
+ }
+
+ [[nodiscard]] bool containsProjectionReceiver() const {
+ return mImpl && mImpl->containsProjectionReceiver();
+ }
+
+ [[nodiscard]] skiapipeline::SkiaDisplayList* asSkiaDl() {
+ return mImpl.get();
+ }
+
+ [[nodiscard]] const skiapipeline::SkiaDisplayList* asSkiaDl() const {
+ return mImpl.get();
+ }
+
+ [[nodiscard]] bool hasVectorDrawables() const {
+ return mImpl && mImpl->hasVectorDrawables();
+ }
+
+ void clear(RenderNode* owningNode = nullptr) {
+ if (mImpl && owningNode && mImpl->reuseDisplayList(owningNode)) {
+ // TODO: This is a bit sketchy to have a unique_ptr temporarily owned twice
+ // Do something to cleanup reuseDisplayList passing itself to the RenderNode
+ mImpl.release();
+ } else {
+ mImpl = nullptr;
+ }
+ }
+
+ [[nodiscard]] size_t getUsedSize() const {
+ return mImpl ? mImpl->getUsedSize() : 0;
+ }
+
+ [[nodiscard]] size_t getAllocatedSize() const {
+ return mImpl ? mImpl->getAllocatedSize() : 0;
+ }
+
+ void output(std::ostream& output, uint32_t level) const {
+ if (mImpl) {
+ mImpl->output(output, level);
+ }
+ }
+
+ [[nodiscard]] bool hasFunctor() const {
+ return mImpl && mImpl->hasFunctor();
+ }
+
+ bool prepareListAndChildren(
+ TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
+ std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) {
+ return mImpl && mImpl->prepareListAndChildren(
+ observer, info, functorsNeedLayer, std::move(childFn));
+ }
+
+ void syncContents(const WebViewSyncData& data) {
+ if (mImpl) {
+ mImpl->syncContents(data);
+ }
+ }
+
+ [[nodiscard]] bool hasText() const {
+ return mImpl && mImpl->hasText();
+ }
+
+ void applyColorTransform(ColorTransform transform) {
+ if (mImpl) {
+ mImpl->mDisplayList.applyColorTransform(transform);
+ }
+ }
+
+private:
+ std::unique_ptr<skiapipeline::SkiaDisplayList> mImpl;
+};
} // namespace uirenderer
} // namespace android
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/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 89e3df7..a6a7b12 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -18,7 +18,6 @@
#include "CanvasTransform.h"
#include "hwui/Bitmap.h"
-#include "hwui/Canvas.h"
#include "utils/Macros.h"
#include "utils/TypeLogic.h"
@@ -29,7 +28,6 @@
#include "SkPaint.h"
#include "SkPath.h"
#include "SkRect.h"
-#include "SkTemplates.h"
#include <vector>
@@ -40,6 +38,11 @@
class FunctorDrawable;
}
+namespace VectorDrawable {
+class Tree;
+}
+typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
+
enum class DisplayListOpType : uint8_t {
#define X(T) T,
#include "DisplayListOps.in"
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 74c70c8..44f54ee 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -70,15 +70,17 @@
RenderNode::~RenderNode() {
ImmediateRemoved observer(nullptr);
deleteDisplayList(observer);
- delete mStagingDisplayList;
LOG_ALWAYS_FATAL_IF(hasLayer(), "layer missed detachment!");
}
-void RenderNode::setStagingDisplayList(DisplayList* displayList) {
- mValid = (displayList != nullptr);
+void RenderNode::setStagingDisplayList(DisplayList&& newData) {
+ mValid = newData.isValid();
mNeedsDisplayListSync = true;
- delete mStagingDisplayList;
- mStagingDisplayList = displayList;
+ mStagingDisplayList = std::move(newData);
+}
+
+void RenderNode::discardStagingDisplayList() {
+ setStagingDisplayList(DisplayList());
}
/**
@@ -101,32 +103,22 @@
properties().debugOutputProperties(output, level + 1);
- if (mDisplayList) {
- mDisplayList->output(output, level);
- }
+ mDisplayList.output(output, level);
output << std::string(level * 2, ' ') << "/RenderNode(" << getName() << " " << this << ")";
output << std::endl;
}
int RenderNode::getUsageSize() {
int size = sizeof(RenderNode);
- if (mStagingDisplayList) {
- size += mStagingDisplayList->getUsedSize();
- }
- if (mDisplayList && mDisplayList != mStagingDisplayList) {
- size += mDisplayList->getUsedSize();
- }
+ size += mStagingDisplayList.getUsedSize();
+ size += mDisplayList.getUsedSize();
return size;
}
int RenderNode::getAllocatedSize() {
int size = sizeof(RenderNode);
- if (mStagingDisplayList) {
- size += mStagingDisplayList->getAllocatedSize();
- }
- if (mDisplayList && mDisplayList != mStagingDisplayList) {
- size += mDisplayList->getAllocatedSize();
- }
+ size += mStagingDisplayList.getAllocatedSize();
+ size += mDisplayList.getAllocatedSize();
return size;
}
@@ -242,9 +234,9 @@
bool willHaveFunctor = false;
if (info.mode == TreeInfo::MODE_FULL && mStagingDisplayList) {
- willHaveFunctor = mStagingDisplayList->hasFunctor();
+ willHaveFunctor = mStagingDisplayList.hasFunctor();
} else if (mDisplayList) {
- willHaveFunctor = mDisplayList->hasFunctor();
+ willHaveFunctor = mDisplayList.hasFunctor();
}
bool childFunctorsNeedLayer =
mProperties.prepareForFunctorPresence(willHaveFunctor, functorsNeedLayer);
@@ -259,8 +251,8 @@
}
if (mDisplayList) {
- info.out.hasFunctors |= mDisplayList->hasFunctor();
- bool isDirty = mDisplayList->prepareListAndChildren(
+ info.out.hasFunctors |= mDisplayList.hasFunctor();
+ bool isDirty = mDisplayList.prepareListAndChildren(
observer, info, childFunctorsNeedLayer,
[](RenderNode* child, TreeObserver& observer, TreeInfo& info,
bool functorsNeedLayer) {
@@ -314,16 +306,15 @@
// Make sure we inc first so that we don't fluctuate between 0 and 1,
// which would thrash the layer cache
if (mStagingDisplayList) {
- mStagingDisplayList->updateChildren([](RenderNode* child) { child->incParentRefCount(); });
+ mStagingDisplayList.updateChildren([](RenderNode* child) { child->incParentRefCount(); });
}
deleteDisplayList(observer, info);
- mDisplayList = mStagingDisplayList;
- mStagingDisplayList = nullptr;
+ mDisplayList = std::move(mStagingDisplayList);
if (mDisplayList) {
WebViewSyncData syncData {
.applyForceDark = info && !info->disableForceDark
};
- mDisplayList->syncContents(syncData);
+ mDisplayList.syncContents(syncData);
handleForceDark(info);
}
}
@@ -333,15 +324,18 @@
return;
}
auto usage = usageHint();
- const auto& children = mDisplayList->mChildNodes;
- if (mDisplayList->hasText()) {
+ FatVector<RenderNode*, 6> children;
+ mDisplayList.updateChildren([&children](RenderNode* node) {
+ children.push_back(node);
+ });
+ if (mDisplayList.hasText()) {
usage = UsageHint::Foreground;
}
if (usage == UsageHint::Unknown) {
if (children.size() > 1) {
usage = UsageHint::Background;
} else if (children.size() == 1 &&
- children.front().getRenderNode()->usageHint() !=
+ children.front()->usageHint() !=
UsageHint::Background) {
usage = UsageHint::Background;
}
@@ -350,7 +344,7 @@
// Crude overlap check
SkRect drawn = SkRect::MakeEmpty();
for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
- const auto& child = iter->getRenderNode();
+ const auto& child = *iter;
// We use stagingProperties here because we haven't yet sync'd the children
SkRect bounds = SkRect::MakeXYWH(child->stagingProperties().getX(), child->stagingProperties().getY(),
child->stagingProperties().getWidth(), child->stagingProperties().getHeight());
@@ -361,7 +355,7 @@
drawn.join(bounds);
}
}
- mDisplayList->mDisplayList.applyColorTransform(
+ mDisplayList.applyColorTransform(
usage == UsageHint::Background ? ColorTransform::Dark : ColorTransform::Light);
}
@@ -378,20 +372,17 @@
void RenderNode::deleteDisplayList(TreeObserver& observer, TreeInfo* info) {
if (mDisplayList) {
- mDisplayList->updateChildren(
+ mDisplayList.updateChildren(
[&observer, info](RenderNode* child) { child->decParentRefCount(observer, info); });
- if (!mDisplayList->reuseDisplayList(this)) {
- delete mDisplayList;
- }
+ mDisplayList.clear(this);
}
- mDisplayList = nullptr;
}
void RenderNode::destroyHardwareResources(TreeInfo* info) {
if (hasLayer()) {
this->setLayerSurface(nullptr);
}
- setStagingDisplayList(nullptr);
+ discardStagingDisplayList();
ImmediateRemoved observer(info);
deleteDisplayList(observer, info);
@@ -402,7 +393,7 @@
this->setLayerSurface(nullptr);
}
if (mDisplayList) {
- mDisplayList->updateChildren([](RenderNode* child) { child->destroyLayers(); });
+ mDisplayList.updateChildren([](RenderNode* child) { child->destroyLayers(); });
}
}
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 6d5e62e..39ea53b 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -100,16 +100,17 @@
// See flags defined in DisplayList.java
enum ReplayFlag { kReplayFlag_ClipChildren = 0x1 };
- void setStagingDisplayList(DisplayList* newData);
+ void setStagingDisplayList(DisplayList&& newData);
+ void discardStagingDisplayList();
void output();
int getUsageSize();
int getAllocatedSize();
- bool isRenderable() const { return mDisplayList && !mDisplayList->isEmpty(); }
+ bool isRenderable() const { return mDisplayList.hasContent(); }
bool hasProjectionReceiver() const {
- return mDisplayList && mDisplayList->containsProjectionReceiver();
+ return mDisplayList.containsProjectionReceiver();
}
const char* getName() const { return mName.string(); }
@@ -168,12 +169,14 @@
bool nothingToDraw() const {
const Outline& outline = properties().getOutline();
- return mDisplayList == nullptr || properties().getAlpha() <= 0 ||
+ return !mDisplayList.isValid() || properties().getAlpha() <= 0 ||
(outline.getShouldClip() && outline.isEmpty()) || properties().getScaleX() == 0 ||
properties().getScaleY() == 0;
}
- const DisplayList* getDisplayList() const { return mDisplayList; }
+ const DisplayList& getDisplayList() const { return mDisplayList; }
+ // TODO: can this be cleaned up?
+ DisplayList& getDisplayList() { return mDisplayList; }
// Note: The position callbacks are relying on the listener using
// the frameNumber to appropriately batch/synchronize these transactions.
@@ -252,8 +255,8 @@
bool mNeedsDisplayListSync;
// WARNING: Do not delete this directly, you must go through deleteDisplayList()!
- DisplayList* mDisplayList;
- DisplayList* mStagingDisplayList;
+ DisplayList mDisplayList;
+ DisplayList mStagingDisplayList;
int64_t mDamageGenerationId;
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 fa7d373..82b7de4 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -53,9 +53,9 @@
LOG_ALWAYS_FATAL("SkiaCanvas cannot be reset as a recording canvas");
}
- virtual uirenderer::DisplayList* finishRecording() override {
+ virtual uirenderer::DisplayList finishRecording() override {
LOG_ALWAYS_FATAL("SkiaCanvas does not produce a DisplayList");
- return nullptr;
+ return uirenderer::DisplayList();
}
virtual void enableZ(bool enableZ) override {
LOG_ALWAYS_FATAL("SkiaCanvas does not support enableZ");
@@ -152,7 +152,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/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h
index 737d605..17b936a 100644
--- a/libs/hwui/WebViewFunctorManager.h
+++ b/libs/hwui/WebViewFunctorManager.h
@@ -19,11 +19,11 @@
#include <private/hwui/WebViewFunctor.h>
#ifdef __ANDROID__ // Layoutlib does not support render thread
#include <renderthread/RenderProxy.h>
-#else
-#include <utils/Log.h>
#endif
#include <utils/LightRefBase.h>
+#include <utils/Log.h>
+#include <utils/StrongPointer.h>
#include <mutex>
#include <vector>
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 d0c996b..184b11e 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -31,7 +31,7 @@
class SkAnimatedImage;
class SkCanvasState;
-class SkRuntimeEffect;
+class SkRuntimeShaderBuilder;
class SkVertices;
namespace minikin {
@@ -118,7 +118,7 @@
virtual void resetRecording(int width, int height,
uirenderer::RenderNode* renderNode = nullptr) = 0;
- virtual uirenderer::DisplayList* finishRecording() = 0;
+ [[nodiscard]] virtual uirenderer::DisplayList finishRecording() = 0;
virtual void enableZ(bool enableZ) = 0;
bool isHighContrastText() const { return uirenderer::Properties::enableHighContrastText; }
@@ -139,7 +139,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/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index eb9885a..05278f2 100755
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -3,11 +3,12 @@
#include "Bitmap.h"
#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColor.h"
+#include "SkColorSpace.h"
#include "SkPixelRef.h"
#include "SkImageEncoder.h"
#include "SkImageInfo.h"
-#include "SkColor.h"
-#include "SkColorSpace.h"
#include "GraphicsJNI.h"
#include "SkStream.h"
#include "SkWebpEncoder.h"
diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp
index 52522a3..cf02051 100644
--- a/libs/hwui/jni/BitmapFactory.cpp
+++ b/libs/hwui/jni/BitmapFactory.cpp
@@ -8,9 +8,11 @@
#include "MimeType.h"
#include "NinePatchPeeker.h"
#include "SkAndroidCodec.h"
+#include "SkCanvas.h"
#include "SkMath.h"
#include "SkPixelRef.h"
#include "SkStream.h"
+#include "SkString.h"
#include "SkUtils.h"
#include "Utils.h"
diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h
index 541d5a5..ba407f2 100644
--- a/libs/hwui/jni/GraphicsJNI.h
+++ b/libs/hwui/jni/GraphicsJNI.h
@@ -24,6 +24,7 @@
namespace skia {
class BitmapRegionDecoder;
}
+class Canvas;
class Paint;
struct Typeface;
}
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 f4877f4..926e233 100644
--- a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
+++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
@@ -98,9 +98,11 @@
canvas->enableZ(reorderEnable);
}
-static jlong android_view_DisplayListCanvas_finishRecording(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr) {
+static void android_view_DisplayListCanvas_finishRecording(
+ CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jlong renderNodePtr) {
Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
- return reinterpret_cast<jlong>(canvas->finishRecording());
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ renderNode->setStagingDisplayList(canvas->finishRecording());
}
static void android_view_DisplayListCanvas_drawRenderNode(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jlong renderNodePtr) {
@@ -142,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);
@@ -150,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) {
@@ -172,7 +175,7 @@
{ "nGetMaximumTextureWidth", "()I", (void*) android_view_DisplayListCanvas_getMaxTextureSize },
{ "nGetMaximumTextureHeight", "()I", (void*) android_view_DisplayListCanvas_getMaxTextureSize },
{ "nEnableZ", "(JZ)V", (void*) android_view_DisplayListCanvas_enableZ },
- { "nFinishRecording", "(J)J", (void*) android_view_DisplayListCanvas_finishRecording },
+ { "nFinishRecording", "(JJ)V", (void*) android_view_DisplayListCanvas_finishRecording },
{ "nDrawRenderNode", "(JJ)V", (void*) android_view_DisplayListCanvas_drawRenderNode },
{ "nDrawTextureLayer", "(JJ)V", (void*) android_view_DisplayListCanvas_drawTextureLayer },
{ "nDrawCircle", "(JJJJJ)V", (void*) android_view_DisplayListCanvas_drawCircleProps },
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index 6f4ba89..8b35d96 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -76,11 +76,9 @@
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&releaseRenderNode));
}
-static void android_view_RenderNode_setDisplayList(JNIEnv* env,
- jobject clazz, jlong renderNodePtr, jlong displayListPtr) {
+static void android_view_RenderNode_discardDisplayList(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- DisplayList* newData = reinterpret_cast<DisplayList*>(displayListPtr);
- renderNode->setStagingDisplayList(newData);
+ renderNode->discardStagingDisplayList();
}
static jboolean android_view_RenderNode_isValid(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
@@ -657,18 +655,11 @@
{ "nAddAnimator", "(JJ)V", (void*) android_view_RenderNode_addAnimator },
{ "nEndAllAnimators", "(J)V", (void*) android_view_RenderNode_endAllAnimators },
{ "nRequestPositionUpdates", "(JLandroid/graphics/RenderNode$PositionUpdateListener;)V", (void*) android_view_RenderNode_requestPositionUpdates },
- { "nSetDisplayList", "(JJ)V", (void*) android_view_RenderNode_setDisplayList },
-
-
-// ----------------------------------------------------------------------------
-// Fast JNI via @CriticalNative annotation in RenderNode.java
-// ----------------------------------------------------------------------------
- { "nSetDisplayList", "(JJ)V", (void*) android_view_RenderNode_setDisplayList },
-
// ----------------------------------------------------------------------------
// Critical JNI via @CriticalNative annotation in RenderNode.java
// ----------------------------------------------------------------------------
+ { "nDiscardDisplayList", "(J)V", (void*) android_view_RenderNode_discardDisplayList },
{ "nIsValid", "(J)Z", (void*) android_view_RenderNode_isValid },
{ "nSetLayerType", "(JI)Z", (void*) android_view_RenderNode_setLayerType },
{ "nGetLayerType", "(J)I", (void*) android_view_RenderNode_getLayerType },
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index f612bce..943423f 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -33,6 +33,7 @@
#include <hwui/Paint.h>
#include <hwui/Typeface.h>
#include <minikin/FontFamily.h>
+#include <minikin/FontFileParser.h>
#include <ui/FatVector.h>
#include <memory>
@@ -233,6 +234,51 @@
///////////////////////////////////////////////////////////////////////////////
+// Fast Native
+static jlong FontFileUtil_getFontRevision(JNIEnv* env, jobject, jobject buffer, jint index) {
+ NPE_CHECK_RETURN_ZERO(env, buffer);
+ const void* fontPtr = env->GetDirectBufferAddress(buffer);
+ if (fontPtr == nullptr) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Not a direct buffer");
+ return 0;
+ }
+ jlong fontSize = env->GetDirectBufferCapacity(buffer);
+ if (fontSize <= 0) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "buffer size must not be zero or negative");
+ return 0;
+ }
+ minikin::FontFileParser parser(fontPtr, fontSize, index);
+ std::optional<uint32_t> revision = parser.getFontRevision();
+ if (!revision.has_value()) {
+ return -1L;
+ }
+ return revision.value();
+}
+
+static jstring FontFileUtil_getFontPostScriptName(JNIEnv* env, jobject, jobject buffer,
+ jint index) {
+ NPE_CHECK_RETURN_ZERO(env, buffer);
+ const void* fontPtr = env->GetDirectBufferAddress(buffer);
+ if (fontPtr == nullptr) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Not a direct buffer");
+ return nullptr;
+ }
+ jlong fontSize = env->GetDirectBufferCapacity(buffer);
+ if (fontSize <= 0) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "buffer size must not be zero or negative");
+ return nullptr;
+ }
+ minikin::FontFileParser parser(fontPtr, fontSize, index);
+ std::optional<std::string> psName = parser.getPostScriptName();
+ if (!psName.has_value()) {
+ return nullptr; // null
+ }
+ return env->NewStringUTF(psName->c_str());
+}
+///////////////////////////////////////////////////////////////////////////////
+
static const JNINativeMethod gFontBuilderMethods[] = {
{ "nInitBuilder", "()J", (void*) Font_Builder_initBuilder },
{ "nAddAxis", "(JIF)V", (void*) Font_Builder_addAxis },
@@ -254,13 +300,21 @@
{ "nGetReleaseFunc", "()J", (void*) FontBufferHelper_getReleaseFunc },
};
+static const JNINativeMethod gFontFileUtilMethods[] = {
+ { "nGetFontRevision", "(Ljava/nio/ByteBuffer;I)J", (void*) FontFileUtil_getFontRevision },
+ { "nGetFontPostScriptName", "(Ljava/nio/ByteBuffer;I)Ljava/lang/String;",
+ (void*) FontFileUtil_getFontPostScriptName },
+};
+
int register_android_graphics_fonts_Font(JNIEnv* env) {
return RegisterMethodsOrDie(env, "android/graphics/fonts/Font$Builder", gFontBuilderMethods,
NELEM(gFontBuilderMethods)) +
RegisterMethodsOrDie(env, "android/graphics/fonts/Font", gFontMethods,
NELEM(gFontMethods)) +
RegisterMethodsOrDie(env, "android/graphics/fonts/NativeFontBufferHelper",
- gFontBufferHelperMethods, NELEM(gFontBufferHelperMethods));
+ gFontBufferHelperMethods, NELEM(gFontBufferHelperMethods)) +
+ RegisterMethodsOrDie(env, "android/graphics/fonts/FontFileUtil", gFontFileUtilMethods,
+ NELEM(gFontFileUtilMethods));
}
namespace fonts {
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/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index 26ff8bf..3580bed 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -29,7 +29,7 @@
*/
class DumpOpsCanvas : public SkCanvas {
public:
- DumpOpsCanvas(std::ostream& output, int level, SkiaDisplayList& displayList)
+ DumpOpsCanvas(std::ostream& output, int level, const SkiaDisplayList& displayList)
: mOutput(output)
, mLevel(level)
, mDisplayList(displayList)
@@ -127,7 +127,7 @@
}
private:
- RenderNodeDrawable* getRenderNodeDrawable(SkDrawable* drawable) {
+ const RenderNodeDrawable* getRenderNodeDrawable(SkDrawable* drawable) {
for (auto& child : mDisplayList.mChildNodes) {
if (drawable == &child) {
return &child;
@@ -147,7 +147,7 @@
std::ostream& mOutput;
int mLevel;
- SkiaDisplayList& mDisplayList;
+ const SkiaDisplayList& mDisplayList;
std::string mIdent;
};
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 1473b3e..070a765 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -61,12 +61,11 @@
SkAutoCanvasRestore acr(canvas, true);
SkMatrix nodeMatrix;
mat4 hwuiMatrix(child.getRecordedMatrix());
- auto childNode = child.getRenderNode();
+ const RenderNode* childNode = child.getRenderNode();
childNode->applyViewPropertyTransforms(hwuiMatrix);
hwuiMatrix.copyTo(nodeMatrix);
canvas->concat(nodeMatrix);
- SkiaDisplayList* childDisplayList = static_cast<SkiaDisplayList*>(
- (const_cast<DisplayList*>(childNode->getDisplayList())));
+ const SkiaDisplayList* childDisplayList = childNode->getDisplayList().asSkiaDl();
if (childDisplayList) {
drawBackwardsProjectedNodes(canvas, *childDisplayList, nestLevel + 1);
}
@@ -144,7 +143,7 @@
return;
}
- SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList();
+ SkiaDisplayList* displayList = renderNode->getDisplayList().asSkiaDl();
SkAutoCanvasRestore acr(canvas, true);
const RenderProperties& properties = this->getNodeProperties();
@@ -213,14 +212,14 @@
if (mComposeLayer) {
setViewProperties(properties, canvas, &alphaMultiplier);
}
- SkiaDisplayList* displayList = (SkiaDisplayList*)mRenderNode->getDisplayList();
+ SkiaDisplayList* displayList = mRenderNode->getDisplayList().asSkiaDl();
displayList->mParentMatrix = canvas->getTotalMatrix();
// TODO should we let the bound of the drawable do this for us?
const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
bool quickRejected = properties.getClipToBounds() && canvas->quickReject(bounds);
if (!quickRejected) {
- SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList();
+ SkiaDisplayList* displayList = renderNode->getDisplayList().asSkiaDl();
const LayerProperties& layerProperties = properties.layerProperties();
// composing a hardware layer
if (renderNode->getLayerSurface() && mComposeLayer) {
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index c63f5d3..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();
}
@@ -172,7 +172,7 @@
new (&allocator) LinearAllocator();
}
-void SkiaDisplayList::output(std::ostream& output, uint32_t level) {
+void SkiaDisplayList::output(std::ostream& output, uint32_t level) const {
DumpOpsCanvas canvas(output, level, *this);
mDisplayList.draw(&canvas);
}
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index f2f19ba..483264f 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -142,7 +142,7 @@
void draw(SkCanvas* canvas) { mDisplayList.draw(canvas); }
- void output(std::ostream& output, uint32_t level);
+ void output(std::ostream& output, uint32_t level) const;
LinearAllocator allocator;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 6e7493c..d14dc36 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -98,7 +98,7 @@
continue;
}
SkASSERT(layerNode->getLayerSurface());
- SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList();
+ SkiaDisplayList* displayList = layerNode->getDisplayList().asSkiaDl();
if (!displayList || displayList->isEmpty()) {
ALOGE("%p drawLayers(%s) : missing drawable", layerNode, layerNode->getName());
return;
@@ -288,7 +288,7 @@
// recurse through the rendernode's children, add any nodes which are layers to the queue.
static void collectLayers(RenderNode* node, LayerUpdateQueue* layers) {
- SkiaDisplayList* dl = (SkiaDisplayList*)node->getDisplayList();
+ SkiaDisplayList* dl = node->getDisplayList().asSkiaDl();
if (dl) {
const auto& prop = node->properties();
if (node->hasLayer()) {
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 7faebda..80c014f 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -55,11 +55,11 @@
SkiaCanvas::reset(&mRecorder);
}
-uirenderer::DisplayList* SkiaRecordingCanvas::finishRecording() {
+uirenderer::DisplayList SkiaRecordingCanvas::finishRecording() {
// close any existing chunks if necessary
enableZ(false);
mRecorder.restoreToCount(1);
- return mDisplayList.release();
+ return uirenderer::DisplayList(std::move(mDisplayList));
}
// ----------------------------------------------------------------------------
@@ -90,9 +90,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) {
@@ -304,10 +304,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 622df43..32c1791 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -43,7 +43,7 @@
initDisplayList(renderNode, width, height);
}
- virtual uirenderer::DisplayList* finishRecording() override;
+ virtual uirenderer::DisplayList finishRecording() override;
virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) override;
virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) override;
@@ -71,7 +71,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/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 771c345..ba6e8ee 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -210,7 +210,8 @@
int left, int top, int right, int bottom,
std::function<void(RenderProperties& props, skiapipeline::SkiaRecordingCanvas& canvas)>
setup,
- const char* name = nullptr, skiapipeline::SkiaDisplayList* displayList = nullptr) {
+ const char* name = nullptr,
+ std::unique_ptr<skiapipeline::SkiaDisplayList> displayList = nullptr) {
sp<RenderNode> node = new RenderNode();
if (name) {
node->setName(name);
@@ -218,7 +219,7 @@
RenderProperties& props = node->mutateStagingProperties();
props.setLeftTopRightBottom(left, top, right, bottom);
if (displayList) {
- node->setStagingDisplayList(displayList);
+ node->setStagingDisplayList(DisplayList(std::move(displayList)));
}
if (setup) {
std::unique_ptr<skiapipeline::SkiaRecordingCanvas> canvas(
@@ -346,13 +347,11 @@
node->mNeedsDisplayListSync = false;
node->syncDisplayList(observer, nullptr);
}
- auto displayList = node->getDisplayList();
+ auto& displayList = node->getDisplayList();
if (displayList) {
- for (auto&& childDr :
- static_cast<skiapipeline::SkiaDisplayList*>(const_cast<DisplayList*>(displayList))
- ->mChildNodes) {
- syncHierarchyPropertiesAndDisplayListImpl(childDr.getRenderNode());
- }
+ displayList.updateChildren([](RenderNode* child) {
+ syncHierarchyPropertiesAndDisplayListImpl(child);
+ });
}
}
diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
index d393c69..ade1ddd 100644
--- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
@@ -45,19 +45,19 @@
void BM_DisplayListCanvas_record_empty(benchmark::State& benchState) {
std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100));
- delete canvas->finishRecording();
+ static_cast<void>(canvas->finishRecording());
while (benchState.KeepRunning()) {
canvas->resetRecording(100, 100);
benchmark::DoNotOptimize(canvas.get());
- delete canvas->finishRecording();
+ static_cast<void>(canvas->finishRecording());
}
}
BENCHMARK(BM_DisplayListCanvas_record_empty);
void BM_DisplayListCanvas_record_saverestore(benchmark::State& benchState) {
std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100));
- delete canvas->finishRecording();
+ static_cast<void>(canvas->finishRecording());
while (benchState.KeepRunning()) {
canvas->resetRecording(100, 100);
@@ -66,20 +66,20 @@
benchmark::DoNotOptimize(canvas.get());
canvas->restore();
canvas->restore();
- delete canvas->finishRecording();
+ static_cast<void>(canvas->finishRecording());
}
}
BENCHMARK(BM_DisplayListCanvas_record_saverestore);
void BM_DisplayListCanvas_record_translate(benchmark::State& benchState) {
std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100));
- delete canvas->finishRecording();
+ static_cast<void>(canvas->finishRecording());
while (benchState.KeepRunning()) {
canvas->resetRecording(100, 100);
canvas->scale(10, 10);
benchmark::DoNotOptimize(canvas.get());
- delete canvas->finishRecording();
+ static_cast<void>(canvas->finishRecording());
}
}
BENCHMARK(BM_DisplayListCanvas_record_translate);
@@ -92,7 +92,7 @@
*/
void BM_DisplayListCanvas_record_simpleBitmapView(benchmark::State& benchState) {
std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100));
- delete canvas->finishRecording();
+ static_cast<void>(canvas->finishRecording());
Paint rectPaint;
sk_sp<Bitmap> iconBitmap(TestUtils::createBitmap(80, 80));
@@ -111,7 +111,7 @@
canvas->restore();
}
benchmark::DoNotOptimize(canvas.get());
- delete canvas->finishRecording();
+ static_cast<void>(canvas->finishRecording());
}
}
BENCHMARK(BM_DisplayListCanvas_record_simpleBitmapView);
@@ -122,7 +122,7 @@
});
std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100));
- delete canvas->finishRecording();
+ static_cast<void>(canvas->finishRecording());
while (benchState.KeepRunning()) {
canvas->resetRecording(200, 200);
@@ -143,7 +143,7 @@
canvas->enableZ(false);
canvas->restoreToCount(clipRestoreCount);
- delete canvas->finishRecording();
+ static_cast<void>(canvas->finishRecording());
}
}
BENCHMARK(BM_DisplayListCanvas_basicViewGroupDraw)->Arg(1)->Arg(5)->Arg(10);
diff --git a/libs/hwui/tests/microbench/RenderNodeBench.cpp b/libs/hwui/tests/microbench/RenderNodeBench.cpp
index 011939a..dd3f737 100644
--- a/libs/hwui/tests/microbench/RenderNodeBench.cpp
+++ b/libs/hwui/tests/microbench/RenderNodeBench.cpp
@@ -16,6 +16,7 @@
#include <benchmark/benchmark.h>
+#include "hwui/Canvas.h"
#include "RenderNode.h"
using namespace android;
@@ -34,7 +35,7 @@
void BM_RenderNode_recordSimple(benchmark::State& state) {
sp<RenderNode> node = new RenderNode();
std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100));
- delete canvas->finishRecording();
+ static_cast<void>(canvas->finishRecording());
while (state.KeepRunning()) {
canvas->resetRecording(100, 100, node.get());
@@ -47,12 +48,12 @@
void BM_RenderNode_recordSimpleWithReuse(benchmark::State& state) {
sp<RenderNode> node = new RenderNode();
std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100));
- delete canvas->finishRecording();
+ static_cast<void>(canvas->finishRecording());
while (state.KeepRunning()) {
canvas->resetRecording(100, 100, node.get());
canvas->drawColor(0x00000000, SkBlendMode::kSrcOver);
- canvas->finishRecording()->reuseDisplayList(node.get());
+ canvas->finishRecording().clear(node.get());
}
}
BENCHMARK(BM_RenderNode_recordSimpleWithReuse);
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 4659a92..61bd646 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -326,7 +326,7 @@
// Check that the VD is in the dislay list, and the layer update queue contains the correct
// damage rect.
- EXPECT_TRUE(rootNode->getDisplayList()->hasVectorDrawables());
+ EXPECT_TRUE(rootNode->getDisplayList().hasVectorDrawables());
ASSERT_FALSE(info.layerUpdateQueue->entries().empty());
EXPECT_EQ(rootNode.get(), info.layerUpdateQueue->entries().at(0).renderNode.get());
EXPECT_EQ(uirenderer::Rect(0, 0, 200, 400), info.layerUpdateQueue->entries().at(0).damage);
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index c63f008..801a294 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -38,12 +38,13 @@
}
TEST(SkiaDisplayList, reset) {
- std::unique_ptr<SkiaDisplayList> skiaDL;
+ DisplayList displayList;
{
SkiaRecordingCanvas canvas{nullptr, 1, 1};
canvas.drawColor(0, SkBlendMode::kSrc);
- skiaDL.reset(canvas.finishRecording());
+ displayList = canvas.finishRecording();
}
+ SkiaDisplayList* skiaDL = displayList.asSkiaDl();
SkCanvas dummyCanvas;
RenderNodeDrawable drawable(nullptr, &dummyCanvas);
diff --git a/location/java/android/location/CorrelationVector.java b/location/java/android/location/CorrelationVector.java
new file mode 100644
index 0000000..eca35dd
--- /dev/null
+++ b/location/java/android/location/CorrelationVector.java
@@ -0,0 +1,228 @@
+/*
+ * 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.location;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Contains info about the correlation output of incoming GNSS signal and a local copy of
+ * its corresponding spreading code at a given frequency offset.
+ *
+ * @hide
+ */
+@SystemApi
+public final class CorrelationVector implements Parcelable {
+
+ private final double mSamplingWidthMeters;
+ private final double mSamplingStartMeters;
+ private final int mFrequencyOffsetMetersPerSecond;
+ @NonNull private final int[] mMagnitude;
+
+ /**
+ * Returns the space between correlation samples in meters.
+ */
+ @FloatRange(from = 0.0f, fromInclusive = false)
+ public double getSamplingWidthMeters() {
+ return mSamplingWidthMeters;
+ }
+
+ /**
+ * Returns the offset of the first sampling bin in meters.
+ *
+ * <p>The following sampling bins are located at positive offsets from this value as follows:
+ * samplingStartMeters, samplingStartMeters + samplingWidthMeters, ... , samplingStartMeters +
+ * (magnitude.size-1) * samplingWidthMeters.
+ *
+ */
+ @FloatRange(from = 0.0f)
+ public double getSamplingStartMeters() {
+ return mSamplingStartMeters;
+ }
+
+ /**
+ * Returns the frequency offset from reported pseudorange rate for this CorrelationVector.
+ */
+ @IntRange(from = 0)
+ public int getFrequencyOffsetMetersPerSecond() {
+ return mFrequencyOffsetMetersPerSecond;
+ }
+
+ /**
+ * Returns the data array representing normalized correlation magnitude values.
+ *
+ * <p>The data are normalized correlation magnitude values from -1 to 1, the reported value must
+ * be encoded as signed 16 bit integer where 1 is represented by 32767 and -1 is represented
+ * by -32768.
+ *
+ */
+ @NonNull
+ public int[] getMagnitude() {
+ return mMagnitude.clone();
+ }
+
+ private CorrelationVector(Builder builder) {
+ Preconditions.checkNotNull(builder.mMagnitude, "Magnitude array must not be null");
+ Preconditions.checkArgumentPositive(builder.mMagnitude.length,
+ "Magnitude array must have non-zero length");
+ Preconditions.checkArgumentNonNegative(builder.mFrequencyOffsetMetersPerSecond,
+ "FrequencyOffsetMetersPerSecond must be non-negative (greater than or equal to 0)");
+ Preconditions.checkArgument(builder.mSamplingWidthMeters > 0.0,
+ "SamplingWidthMeters must be positive (greater than 0)");
+ Preconditions.checkArgument(builder.mSamplingStartMeters >= 0.0,
+ "SamplingStartMeters must be non-negative (greater than or equal to 0)");
+ mMagnitude = builder.mMagnitude;
+ mFrequencyOffsetMetersPerSecond = builder.mFrequencyOffsetMetersPerSecond;
+ mSamplingWidthMeters = builder.mSamplingWidthMeters;
+ mSamplingStartMeters = builder.mSamplingStartMeters;
+ }
+
+ private CorrelationVector(Parcel in) {
+ mSamplingWidthMeters = in.readDouble();
+ mSamplingStartMeters = in.readDouble();
+ mFrequencyOffsetMetersPerSecond = in.readInt();
+ mMagnitude = new int[in.readInt()];
+ in.readIntArray(mMagnitude);
+ }
+
+ /*
+ * Method definitions to support Parcelable operations.
+ */
+ public static final @NonNull Parcelable.Creator<CorrelationVector> CREATOR =
+ new Parcelable.Creator<CorrelationVector>() {
+ @Override
+ public CorrelationVector createFromParcel(Parcel parcel) {
+ return new CorrelationVector(parcel);
+ }
+
+ @Override
+ public CorrelationVector[] newArray(int size) {
+ return new CorrelationVector[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "CorrelationVector{"
+ + "FrequencyOffsetMetersPerSecond=" + mFrequencyOffsetMetersPerSecond
+ + ", SamplingWidthMeters=" + mSamplingWidthMeters
+ + ", SamplingStartMeters=" + mSamplingStartMeters
+ + ", Magnitude=" + Arrays.toString(mMagnitude)
+ + '}';
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeDouble(mSamplingWidthMeters);
+ dest.writeDouble(mSamplingStartMeters);
+ dest.writeInt(mFrequencyOffsetMetersPerSecond);
+ dest.writeInt(mMagnitude.length);
+ dest.writeIntArray(mMagnitude);
+ }
+
+ /**
+ * Returns true if this {@link CorrelationVector} is equivalent to the given object.
+ * Returns false otherwise.
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (object == this) {
+ return true;
+ }
+ if (!(object instanceof CorrelationVector)) {
+ return false;
+ }
+ CorrelationVector c = (CorrelationVector) object;
+ return Arrays.equals(mMagnitude, c.getMagnitude())
+ && Double.compare(mSamplingWidthMeters, c.getSamplingWidthMeters()) == 0
+ && Double.compare(mSamplingStartMeters, c.getSamplingStartMeters()) == 0
+ && Integer.compare(mFrequencyOffsetMetersPerSecond,
+ c.getFrequencyOffsetMetersPerSecond()) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSamplingWidthMeters, mSamplingStartMeters,
+ mFrequencyOffsetMetersPerSecond, Arrays.hashCode(mMagnitude));
+ }
+
+ /**
+ * Builder class for CorrelationVector.
+ */
+ public static final class Builder {
+
+ private double mSamplingWidthMeters;
+ private double mSamplingStartMeters;
+ private int mFrequencyOffsetMetersPerSecond;
+ @NonNull private int[] mMagnitude;
+
+ /** Sets the space between correlation samples in meters. */
+ @NonNull
+ public Builder setSamplingWidthMeters(
+ @FloatRange(from = 0.0f, fromInclusive = false) double samplingWidthMeters) {
+ mSamplingWidthMeters = samplingWidthMeters;
+ return this;
+ }
+
+ /** Sets the offset of the first sampling bin in meters. */
+ @NonNull
+ public Builder setSamplingStartMeters(@FloatRange(from = 0.0f) double samplingStartMeters) {
+ mSamplingStartMeters = samplingStartMeters;
+ return this;
+ }
+
+ /** Sets the frequency offset from reported pseudorange rate for this CorrelationVector */
+ @NonNull
+ public Builder setFrequencyOffsetMetersPerSecond(
+ @IntRange(from = 0) int frequencyOffsetMetersPerSecond) {
+ mFrequencyOffsetMetersPerSecond = frequencyOffsetMetersPerSecond;
+ return this;
+ }
+
+ /** Sets the data array representing normalized correlation magnitude values. */
+ @NonNull
+ public Builder setMagnitude(@NonNull int[] magnitude) {
+ mMagnitude = magnitude;
+ return this;
+ }
+
+ /**
+ * Build CorrelationVector object.
+ *
+ * @return instance of CorrelationVector
+ */
+ @NonNull
+ public CorrelationVector build() {
+ return new CorrelationVector(this);
+ }
+ }
+}
diff --git a/location/java/android/location/GnssCapabilities.java b/location/java/android/location/GnssCapabilities.java
index b650a9f..a5e2815 100644
--- a/location/java/android/location/GnssCapabilities.java
+++ b/location/java/android/location/GnssCapabilities.java
@@ -58,6 +58,8 @@
/** @hide */
public static final int TOP_HAL_CAPABILITY_ANTENNA_INFO = 2048;
/** @hide */
+ public static final int TOP_HAL_CAPABILITY_CORRELATION_VECTOR = 4096;
+ /** @hide */
public static final int TOP_HAL_CAPABILITY_SATELLITE_PVT = 8192;
/** @hide */
@@ -67,7 +69,8 @@
TOP_HAL_CAPABILITY_MEASUREMENTS, TOP_HAL_CAPABILITY_NAV_MESSAGES,
TOP_HAL_CAPABILITY_LOW_POWER_MODE, TOP_HAL_CAPABILITY_SATELLITE_BLOCKLIST,
TOP_HAL_CAPABILITY_MEASUREMENT_CORRECTIONS, TOP_HAL_CAPABILITY_ANTENNA_INFO,
- TOP_HAL_CAPABILITY_SATELLITE_PVT})
+ TOP_HAL_CAPABILITY_CORRELATION_VECTOR, TOP_HAL_CAPABILITY_SATELLITE_PVT})
+
@Retention(RetentionPolicy.SOURCE)
public @interface TopHalCapabilityFlags {}
@@ -337,6 +340,17 @@
}
/**
+ * Returns {@code true} if GNSS chipset supports correlation vectors as part of measurements
+ * outputs, {@code false} otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean hasMeasurementCorrelationVectors() {
+ return (mTopFlags & TOP_HAL_CAPABILITY_CORRELATION_VECTOR) != 0;
+ }
+
+ /**
* Returns {@code true} if GNSS chipset supports line-of-sight satellite identification
* measurement corrections, {@code false} otherwise.
*
@@ -533,6 +547,9 @@
if (hasAntennaInfo()) {
builder.append("ANTENNA_INFO ");
}
+ if (hasMeasurementCorrelationVectors()) {
+ builder.append("MEASUREMENT_CORRELATION_VECTORS ");
+ }
if (hasMeasurementCorrectionsLosSats()) {
builder.append("LOS_SATS ");
}
@@ -720,6 +737,17 @@
}
/**
+ * Sets correlation vector capability.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setHasMeasurementCorrelationVectors(boolean capable) {
+ mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_CORRELATION_VECTOR, capable);
+ return this;
+ }
+
+ /**
* Sets measurement corrections line-of-sight satellites capabilitity.
*
* @hide
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 5509a6c6..3d188c0 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -38,6 +38,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
/**
* A class representing a GNSS satellite measurement, containing raw and computed information.
@@ -70,6 +73,7 @@
private double mSatelliteInterSignalBiasNanos;
private double mSatelliteInterSignalBiasUncertaintyNanos;
@Nullable private SatellitePvt mSatellitePvt;
+ @Nullable private Collection<CorrelationVector> mReadOnlyCorrelationVectors;
// The following enumerations must be in sync with the values declared in GNSS HAL.
@@ -77,6 +81,7 @@
private static final int HAS_CODE_TYPE = (1 << 14);
private static final int HAS_BASEBAND_CN0 = (1 << 15);
private static final int HAS_SATELLITE_PVT = (1 << 20);
+ private static final int HAS_CORRELATION_VECTOR = (1 << 21);
/**
* The status of the multipath indicator.
@@ -173,8 +178,8 @@
* @hide
*/
@IntDef(flag = true, prefix = { "ADR_STATE_" }, value = {
- ADR_STATE_VALID, ADR_STATE_RESET, ADR_STATE_CYCLE_SLIP, ADR_STATE_HALF_CYCLE_RESOLVED,
- ADR_STATE_HALF_CYCLE_REPORTED
+ ADR_STATE_UNKNOWN, ADR_STATE_VALID, ADR_STATE_RESET, ADR_STATE_CYCLE_SLIP,
+ ADR_STATE_HALF_CYCLE_RESOLVED, ADR_STATE_HALF_CYCLE_REPORTED
})
@Retention(RetentionPolicy.SOURCE)
public @interface AdrState {}
@@ -279,6 +284,7 @@
mSatelliteInterSignalBiasUncertaintyNanos =
measurement.mSatelliteInterSignalBiasUncertaintyNanos;
mSatellitePvt = measurement.mSatellitePvt;
+ mReadOnlyCorrelationVectors = measurement.mReadOnlyCorrelationVectors;
}
/**
@@ -1712,6 +1718,7 @@
*
* <p>The value is only available if {@link #hasSatellitePvt()} is
* {@code true}.
+ *
* @hide
*/
@Nullable
@@ -1745,6 +1752,58 @@
resetFlag(HAS_SATELLITE_PVT);
}
+ /**
+ * Returns {@code true} if {@link #getCorrelationVectors()} is available,
+ * {@code false} otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean hasCorrelationVectors() {
+ return isFlagSet(HAS_CORRELATION_VECTOR);
+ }
+
+ /**
+ * Gets read-only collection of CorrelationVector with each CorrelationVector corresponding to a
+ * frequency offset.
+ *
+ * <p>To represent correlation values over a 2D spaces (delay and frequency), a
+ * CorrelationVector is required per frequency offset, and each CorrelationVector contains
+ * correlation values at equally spaced spatial offsets.
+ *
+ * @hide
+ */
+ @Nullable
+ @SystemApi
+ public Collection<CorrelationVector> getCorrelationVectors() {
+ return mReadOnlyCorrelationVectors;
+ }
+
+ /**
+ * Sets the CorrelationVectors.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setCorrelationVectors(@Nullable Collection<CorrelationVector> correlationVectors) {
+ if (correlationVectors == null || correlationVectors.isEmpty()) {
+ resetCorrelationVectors();
+ } else {
+ setFlag(HAS_CORRELATION_VECTOR);
+ mReadOnlyCorrelationVectors = Collections.unmodifiableCollection(correlationVectors);
+ }
+ }
+
+ /**
+ * Resets the CorrelationVectors.
+ *
+ * @hide
+ */
+ @TestApi
+ public void resetCorrelationVectors() {
+ resetFlag(HAS_CORRELATION_VECTOR);
+ mReadOnlyCorrelationVectors = null;
+ }
public static final @NonNull Creator<GnssMeasurement> CREATOR = new Creator<GnssMeasurement>() {
@Override
@@ -1781,6 +1840,15 @@
ClassLoader classLoader = getClass().getClassLoader();
gnssMeasurement.mSatellitePvt = parcel.readParcelable(classLoader);
}
+ if (gnssMeasurement.hasCorrelationVectors()) {
+ CorrelationVector[] correlationVectorsArray =
+ new CorrelationVector[parcel.readInt()];
+ parcel.readTypedArray(correlationVectorsArray, CorrelationVector.CREATOR);
+ Collection<CorrelationVector> corrVecCollection =
+ Arrays.asList(correlationVectorsArray);
+ gnssMeasurement.mReadOnlyCorrelationVectors =
+ Collections.unmodifiableCollection(corrVecCollection);
+ }
return gnssMeasurement;
}
@@ -1821,6 +1889,13 @@
if (hasSatellitePvt()) {
parcel.writeParcelable(mSatellitePvt, flags);
}
+ if (hasCorrelationVectors()) {
+ int correlationVectorCount = mReadOnlyCorrelationVectors.size();
+ CorrelationVector[] correlationVectorArray =
+ mReadOnlyCorrelationVectors.toArray(new CorrelationVector[correlationVectorCount]);
+ parcel.writeInt(correlationVectorArray.length);
+ parcel.writeTypedArray(correlationVectorArray, flags);
+ }
}
@Override
@@ -1928,6 +2003,13 @@
builder.append(mSatellitePvt.toString());
}
+ if (hasCorrelationVectors()) {
+ for (CorrelationVector correlationVector : mReadOnlyCorrelationVectors) {
+ builder.append(correlationVector.toString());
+ builder.append("\n");
+ }
+ }
+
return builder.toString();
}
@@ -1958,6 +2040,7 @@
resetSatelliteInterSignalBiasNanos();
resetSatelliteInterSignalBiasUncertaintyNanos();
resetSatellitePvt();
+ resetCorrelationVectors();
}
private void setFlag(int flag) {
diff --git a/location/java/android/location/GnssMeasurementRequest.java b/location/java/android/location/GnssMeasurementRequest.java
index 613f591..f509252 100644
--- a/location/java/android/location/GnssMeasurementRequest.java
+++ b/location/java/android/location/GnssMeasurementRequest.java
@@ -17,20 +17,38 @@
package android.location;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* This class contains extra parameters to pass in a GNSS measurement request.
*/
public final class GnssMeasurementRequest implements Parcelable {
+ private final boolean mCorrelationVectorOutputsEnabled;
private final boolean mFullTracking;
/**
* Creates a {@link GnssMeasurementRequest} with a full list of parameters.
*/
- private GnssMeasurementRequest(boolean fullTracking) {
+ private GnssMeasurementRequest(boolean fullTracking, boolean correlationVectorOutputsEnabled) {
mFullTracking = fullTracking;
+ mCorrelationVectorOutputsEnabled = correlationVectorOutputsEnabled;
+ }
+
+ /**
+ * Represents whether to enable correlation vector outputs.
+ *
+ * <p>If true, enable correlation vectors as part of the raw GNSS measurements outputs.
+ * If false, disable correlation vectors.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isCorrelationVectorOutputsEnabled() {
+ return mCorrelationVectorOutputsEnabled;
}
/**
@@ -56,7 +74,7 @@
@Override
@NonNull
public GnssMeasurementRequest createFromParcel(@NonNull Parcel parcel) {
- return new GnssMeasurementRequest(parcel.readBoolean());
+ return new GnssMeasurementRequest(parcel.readBoolean(), parcel.readBoolean());
}
@Override
@@ -68,6 +86,7 @@
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeBoolean(mFullTracking);
+ parcel.writeBoolean(mCorrelationVectorOutputsEnabled);
}
@NonNull
@@ -78,6 +97,9 @@
if (mFullTracking) {
s.append("FullTracking");
}
+ if (mCorrelationVectorOutputsEnabled) {
+ s.append(", CorrelationVectorOutPuts");
+ }
s.append(']');
return s.toString();
}
@@ -90,13 +112,15 @@
GnssMeasurementRequest other = (GnssMeasurementRequest) obj;
if (mFullTracking != other.mFullTracking) return false;
-
+ if (mCorrelationVectorOutputsEnabled != other.mCorrelationVectorOutputsEnabled) {
+ return false;
+ }
return true;
}
@Override
public int hashCode() {
- return mFullTracking ? 1 : 0;
+ return Objects.hash(mFullTracking, mCorrelationVectorOutputsEnabled);
}
@Override
@@ -106,6 +130,7 @@
/** Builder for {@link GnssMeasurementRequest} */
public static final class Builder {
+ private boolean mCorrelationVectorOutputsEnabled;
private boolean mFullTracking;
/**
@@ -118,10 +143,25 @@
* Constructs a {@link Builder} instance by copying a {@link GnssMeasurementRequest}.
*/
public Builder(@NonNull GnssMeasurementRequest request) {
+ mCorrelationVectorOutputsEnabled = request.isCorrelationVectorOutputsEnabled();
mFullTracking = request.isFullTracking();
}
/**
+ * Set the value of whether to enable correlation vector outputs, which is false by default.
+ *
+ * <p>If true, enable correlation vectors as part of the raw GNSS measurements outputs.
+ * If false, disable correlation vectors.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull public Builder setCorrelationVectorOutputsEnabled(boolean value) {
+ mCorrelationVectorOutputsEnabled = value;
+ return this;
+ }
+
+ /**
* Set the value of whether to enable full GNSS tracking, which is false by default.
*
* <p>If true, GNSS chipset switches off duty cycling. In such a mode, no clock
@@ -146,7 +186,7 @@
/** Builds a {@link GnssMeasurementRequest} instance as specified by this builder. */
@NonNull
public GnssMeasurementRequest build() {
- return new GnssMeasurementRequest(mFullTracking);
+ return new GnssMeasurementRequest(mFullTracking, mCorrelationVectorOutputsEnabled);
}
}
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 2dc9eb4..0ce1ad0 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -3171,7 +3171,9 @@
for (GnssMeasurementRequest request : requests) {
if (request.isFullTracking()) {
builder.setFullTracking(true);
- break;
+ }
+ if (request.isCorrelationVectorOutputsEnabled()) {
+ builder.setCorrelationVectorOutputsEnabled(true);
}
}
diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java
index 1027f9c..a6a0e7a 100644
--- a/location/java/android/location/LocationManagerInternal.java
+++ b/location/java/android/location/LocationManagerInternal.java
@@ -80,4 +80,12 @@
*/
// TODO: there is no reason for this to exist as part of any API. move all the logic into gnss
public abstract void sendNiResponse(int notifId, int userResponse);
+
+ /**
+ * Returns the GNSS provided time.
+ *
+ * @return LocationTime object that includes the current time, according to the GNSS location
+ * provider, and the elapsed nanos since boot the current time was computed at.
+ */
+ public abstract @Nullable LocationTime getGnssTimeMillis();
}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index ae97a71..607c8f1 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -25,6 +25,7 @@
import android.graphics.SurfaceTexture;
import android.hardware.HardwareBuffer;
import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.metrics.PlaybackComponent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -1538,7 +1539,8 @@
</tbody>
</table>
*/
-final public class MediaCodec {
+final public class MediaCodec implements PlaybackComponent {
+
/**
* Per buffer metadata includes an offset and size specifying
* the range of valid data in the associated codec (output) buffer.
@@ -1680,6 +1682,7 @@
private MediaCodecInfo mCodecInfo;
private final Object mCodecInfoLock = new Object();
private MediaCrypto mCrypto;
+ private String mPlaybackId;
private static final int EVENT_CALLBACK = 1;
private static final int EVENT_SET_CALLBACK = 2;
@@ -1690,6 +1693,23 @@
private static final int CB_ERROR = 3;
private static final int CB_OUTPUT_FORMAT_CHANGE = 4;
+
+ /**
+ * @hide
+ */
+ @Override
+ public void setPlaybackId(@NonNull String playbackId) {
+ // TODO: add a native method to pass the ID to the native code for logging.
+ mPlaybackId = playbackId;
+ }
+ /**
+ * @hide
+ */
+ @Override
+ public String getPlaybackId() {
+ return mPlaybackId;
+ }
+
private class EventHandler extends Handler {
private MediaCodec mCodec;
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 221147d..0c73348 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -218,7 +218,7 @@
/** @return an error string if the format would not allow Privileged playbackCapture
* null otherwise
* @hide */
- public static String canBeUsedForPrivilegedCapture(AudioFormat format) {
+ public static String canBeUsedForPrivilegedMediaCapture(AudioFormat format) {
int sampleRate = format.getSampleRate();
if (sampleRate > PRIVILEDGED_CAPTURE_MAX_SAMPLE_RATE || sampleRate <= 0) {
return "Privileged audio capture sample rate " + sampleRate
@@ -448,8 +448,8 @@
}
}
}
- if (mRule.allowPrivilegedPlaybackCapture()) {
- String error = AudioMix.canBeUsedForPrivilegedCapture(mFormat);
+ if (mRule.allowPrivilegedMediaPlaybackCapture()) {
+ String error = AudioMix.canBeUsedForPrivilegedMediaCapture(mFormat);
if (error != null) {
throw new IllegalArgumentException(error);
}
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index de15313..1f07705 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -46,11 +46,11 @@
public class AudioMixingRule {
private AudioMixingRule(int mixType, ArrayList<AudioMixMatchCriterion> criteria,
- boolean allowPrivilegedPlaybackCapture,
+ boolean allowPrivilegedMediaPlaybackCapture,
boolean voiceCommunicationCaptureAllowed) {
mCriteria = criteria;
mTargetMixType = mixType;
- mAllowPrivilegedPlaybackCapture = allowPrivilegedPlaybackCapture;
+ mAllowPrivilegedPlaybackCapture = allowPrivilegedMediaPlaybackCapture;
mVoiceCommunicationCaptureAllowed = voiceCommunicationCaptureAllowed;
}
@@ -204,13 +204,17 @@
private final ArrayList<AudioMixMatchCriterion> mCriteria;
/** @hide */
public ArrayList<AudioMixMatchCriterion> getCriteria() { return mCriteria; }
+ /** Indicates that this rule is intended to capture media or game playback by a system component
+ * with permission CAPTURE_MEDIA_OUTPUT or CAPTURE_AUDIO_OUTPUT.
+ */
+ //TODO b/177061175: rename to mAllowPrivilegedMediaPlaybackCapture
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mAllowPrivilegedPlaybackCapture = false;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mVoiceCommunicationCaptureAllowed = false;
/** @hide */
- public boolean allowPrivilegedPlaybackCapture() {
+ public boolean allowPrivilegedMediaPlaybackCapture() {
return mAllowPrivilegedPlaybackCapture;
}
@@ -311,7 +315,7 @@
public static class Builder {
private ArrayList<AudioMixMatchCriterion> mCriteria;
private int mTargetMixType = AudioMix.MIX_TYPE_INVALID;
- private boolean mAllowPrivilegedPlaybackCapture = false;
+ private boolean mAllowPrivilegedMediaPlaybackCapture = false;
// This value should be set internally according to a permission check
private boolean mVoiceCommunicationCaptureAllowed = false;
@@ -434,7 +438,7 @@
* @return the same Builder instance.
*/
public @NonNull Builder allowPrivilegedPlaybackCapture(boolean allow) {
- mAllowPrivilegedPlaybackCapture = allow;
+ mAllowPrivilegedMediaPlaybackCapture = allow;
return this;
}
@@ -639,7 +643,7 @@
*/
public AudioMixingRule build() {
return new AudioMixingRule(mTargetMixType, mCriteria,
- mAllowPrivilegedPlaybackCapture, mVoiceCommunicationCaptureAllowed);
+ mAllowPrivilegedMediaPlaybackCapture, mVoiceCommunicationCaptureAllowed);
}
}
}
diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
index 697d80c..ede68bd 100644
--- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java
+++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
@@ -97,7 +97,7 @@
dest.writeInt(mix.getFormat().getEncoding());
dest.writeInt(mix.getFormat().getChannelMask());
// write opt-out respect
- dest.writeBoolean(mix.getRule().allowPrivilegedPlaybackCapture());
+ dest.writeBoolean(mix.getRule().allowPrivilegedMediaPlaybackCapture());
// write voice communication capture allowed flag
dest.writeBoolean(mix.getRule().voiceCommunicationCaptureAllowed());
// write mix rules
@@ -172,7 +172,7 @@
textDump += " channels=0x";
textDump += Integer.toHexString(mix.getFormat().getChannelMask()).toUpperCase() + "\n";
textDump += " ignore playback capture opt out="
- + mix.getRule().allowPrivilegedPlaybackCapture() + "\n";
+ + mix.getRule().allowPrivilegedMediaPlaybackCapture() + "\n";
textDump += " allow voice communication capture="
+ mix.getRule().voiceCommunicationCaptureAllowed() + "\n";
// write mix rules
diff --git a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl
index 47debe9..e55678d9 100644
--- a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl
+++ b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl
@@ -16,7 +16,11 @@
package android.media.metrics;
+import android.media.metrics.NetworkEvent;
+import android.media.metrics.PlaybackErrorEvent;
import android.media.metrics.PlaybackMetrics;
+import android.media.metrics.PlaybackStateEvent;
+import android.media.metrics.TrackChangeEvent;
/**
* Interface to the playback manager service.
@@ -25,4 +29,8 @@
interface IPlaybackMetricsManager {
void reportPlaybackMetrics(in String sessionId, in PlaybackMetrics metrics, int userId);
String getSessionId(int userId);
+ void reportNetworkEvent(in String sessionId, in NetworkEvent event, int userId);
+ void reportPlaybackErrorEvent(in String sessionId, in PlaybackErrorEvent event, int userId);
+ void reportPlaybackStateEvent(in String sessionId, in PlaybackStateEvent event, int userId);
+ void reportTrackChangeEvent(in String sessionId, in TrackChangeEvent event, int userId);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl b/media/java/android/media/metrics/NetworkEvent.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/NetworkEvent.aidl
index edf96dd..2b7fa02 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
+++ b/media/java/android/media/metrics/NetworkEvent.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 NetworkEvent;
diff --git a/media/java/android/media/metrics/NetworkEvent.java b/media/java/android/media/metrics/NetworkEvent.java
new file mode 100644
index 0000000..a330bc0
--- /dev/null
+++ b/media/java/android/media/metrics/NetworkEvent.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.metrics;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Playback network event.
+ * @hide
+ */
+public final class NetworkEvent implements Parcelable {
+ public static final int NETWORK_TYPE_NONE = 0;
+ public static final int NETWORK_TYPE_OTHER = 1;
+ public static final int NETWORK_TYPE_WIFI = 2;
+ public static final int NETWORK_TYPE_ETHERNET = 3;
+ public static final int NETWORK_TYPE_2G = 4;
+ public static final int NETWORK_TYPE_3G = 5;
+ public static final int NETWORK_TYPE_4G = 6;
+ public static final int NETWORK_TYPE_5G_NSA = 7;
+ public static final int NETWORK_TYPE_5G_SA = 8;
+
+ private final int mType;
+ private final long mTimeSincePlaybackCreatedMillis;
+
+ /** @hide */
+ @IntDef(prefix = "NETWORK_TYPE_", value = {
+ NETWORK_TYPE_NONE,
+ NETWORK_TYPE_OTHER,
+ NETWORK_TYPE_WIFI,
+ NETWORK_TYPE_ETHERNET,
+ NETWORK_TYPE_2G,
+ NETWORK_TYPE_3G,
+ NETWORK_TYPE_4G,
+ NETWORK_TYPE_5G_NSA,
+ NETWORK_TYPE_5G_SA
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NetworkType {}
+
+ /**
+ * Network type to string.
+ */
+ public static String networkTypeToString(@NetworkType int value) {
+ switch (value) {
+ case NETWORK_TYPE_NONE:
+ return "NETWORK_TYPE_NONE";
+ case NETWORK_TYPE_OTHER:
+ return "NETWORK_TYPE_OTHER";
+ case NETWORK_TYPE_WIFI:
+ return "NETWORK_TYPE_WIFI";
+ case NETWORK_TYPE_ETHERNET:
+ return "NETWORK_TYPE_ETHERNET";
+ case NETWORK_TYPE_2G:
+ return "NETWORK_TYPE_2G";
+ case NETWORK_TYPE_3G:
+ return "NETWORK_TYPE_3G";
+ case NETWORK_TYPE_4G:
+ return "NETWORK_TYPE_4G";
+ case NETWORK_TYPE_5G_NSA:
+ return "NETWORK_TYPE_5G_NSA";
+ case NETWORK_TYPE_5G_SA:
+ return "NETWORK_TYPE_5G_SA";
+ default:
+ return Integer.toHexString(value);
+ }
+ }
+
+ /**
+ * Creates a new NetworkEvent.
+ *
+ * @hide
+ */
+ public NetworkEvent(@NetworkType int type, long timeSincePlaybackCreatedMillis) {
+ this.mType = type;
+ this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+ }
+
+ @NetworkType
+ public int getType() {
+ return mType;
+ }
+
+ public long getTimeSincePlaybackCreatedMillis() {
+ return mTimeSincePlaybackCreatedMillis;
+ }
+
+ @Override
+ public String toString() {
+ return "NetworkEvent { "
+ + "type = " + mType + ", "
+ + "timeSincePlaybackCreatedMillis = " + mTimeSincePlaybackCreatedMillis
+ + " }";
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ NetworkEvent that = (NetworkEvent) o;
+ return mType == that.mType
+ && mTimeSincePlaybackCreatedMillis == that.mTimeSincePlaybackCreatedMillis;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mType, mTimeSincePlaybackCreatedMillis);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeLong(mTimeSincePlaybackCreatedMillis);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ /* package-private */ NetworkEvent(@NonNull android.os.Parcel in) {
+ int type = in.readInt();
+ long timeSincePlaybackCreatedMillis = in.readLong();
+
+ this.mType = type;
+ this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+ }
+
+ public static final @NonNull Parcelable.Creator<NetworkEvent> CREATOR =
+ new Parcelable.Creator<NetworkEvent>() {
+ @Override
+ public NetworkEvent[] newArray(int size) {
+ return new NetworkEvent[size];
+ }
+
+ @Override
+ public NetworkEvent createFromParcel(@NonNull Parcel in) {
+ return new NetworkEvent(in);
+ }
+ };
+
+ /**
+ * A builder for {@link NetworkEvent}
+ */
+ public static final class Builder {
+ private int mType;
+ private long mTimeSincePlaybackCreatedMillis;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @hide
+ */
+ public Builder() {
+ }
+
+ /**
+ * Sets network type.
+ */
+ public @NonNull Builder setType(@NetworkType int value) {
+ mType = value;
+ return this;
+ }
+
+ /**
+ * Sets timestamp since the creation in milliseconds.
+ */
+ public @NonNull Builder setTimeSincePlaybackCreatedMillis(long value) {
+ mTimeSincePlaybackCreatedMillis = value;
+ return this;
+ }
+
+ /** Builds the instance. */
+ public @NonNull NetworkEvent build() {
+ NetworkEvent o = new NetworkEvent(
+ mType,
+ mTimeSincePlaybackCreatedMillis);
+ return o;
+ }
+ }
+}
diff --git a/media/java/android/media/metrics/PlaybackComponent.java b/media/java/android/media/metrics/PlaybackComponent.java
new file mode 100644
index 0000000..625dd0a
--- /dev/null
+++ b/media/java/android/media/metrics/PlaybackComponent.java
@@ -0,0 +1,36 @@
+/*
+ * 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.NonNull;
+
+/**
+ * Interface for playback related components used by playback metrics.
+ * @hide
+ */
+public interface PlaybackComponent {
+
+ /**
+ * Sets the playback ID of the component.
+ */
+ void setPlaybackId(@NonNull String playbackId);
+
+ /**
+ * Gets playback ID.
+ */
+ @NonNull String getPlaybackId();
+}
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl b/media/java/android/media/metrics/PlaybackErrorEvent.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/PlaybackErrorEvent.aidl
index edf96dd..b0d6b4b 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
+++ b/media/java/android/media/metrics/PlaybackErrorEvent.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 PlaybackErrorEvent;
diff --git a/media/java/android/media/metrics/PlaybackErrorEvent.java b/media/java/android/media/metrics/PlaybackErrorEvent.java
new file mode 100644
index 0000000..db70005
--- /dev/null
+++ b/media/java/android/media/metrics/PlaybackErrorEvent.java
@@ -0,0 +1,234 @@
+/*
+ * 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 error event.
+ * @hide
+ */
+public final class PlaybackErrorEvent implements Parcelable {
+ public static final int ERROR_CODE_UNKNOWN = 0;
+ public static final int ERROR_CODE_OTHER = 1;
+ public static final int ERROR_CODE_RUNTIME = 2;
+
+ private final @Nullable String mExceptionStack;
+ private final int mErrorCode;
+ private final int mSubErrorCode;
+ private final long mTimeSincePlaybackCreatedMillis;
+
+
+ /** @hide */
+ // TODO: more error types
+ @IntDef(prefix = "ERROR_CODE_", value = {
+ ERROR_CODE_UNKNOWN,
+ ERROR_CODE_OTHER,
+ ERROR_CODE_RUNTIME
+ })
+ @Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+ public @interface ErrorCode {}
+
+ /**
+ * Creates a new PlaybackErrorEvent.
+ *
+ * @hide
+ */
+ public PlaybackErrorEvent(
+ @Nullable String exceptionStack,
+ int errorCode,
+ int subErrorCode,
+ long timeSincePlaybackCreatedMillis) {
+ this.mExceptionStack = exceptionStack;
+ this.mErrorCode = errorCode;
+ this.mSubErrorCode = subErrorCode;
+ this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+ }
+
+ /** @hide */
+ @Nullable
+ public String getExceptionStack() {
+ return mExceptionStack;
+ }
+
+ @ErrorCode
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+
+ public int getSubErrorCode() {
+ return mSubErrorCode;
+ }
+
+ public long getTimeSincePlaybackCreatedMillis() {
+ return mTimeSincePlaybackCreatedMillis;
+ }
+
+ @Override
+ public String toString() {
+ return "PlaybackErrorEvent { "
+ + "exceptionStack = " + mExceptionStack + ", "
+ + "errorCode = " + mErrorCode + ", "
+ + "subErrorCode = " + mSubErrorCode + ", "
+ + "timeSincePlaybackCreatedMillis = " + mTimeSincePlaybackCreatedMillis
+ + " }";
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PlaybackErrorEvent that = (PlaybackErrorEvent) o;
+ return Objects.equals(mExceptionStack, that.mExceptionStack)
+ && mErrorCode == that.mErrorCode
+ && mSubErrorCode == that.mSubErrorCode
+ && mTimeSincePlaybackCreatedMillis == that.mTimeSincePlaybackCreatedMillis;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mExceptionStack, mErrorCode, mSubErrorCode,
+ mTimeSincePlaybackCreatedMillis);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ byte flg = 0;
+ if (mExceptionStack != null) flg |= 0x1;
+ dest.writeByte(flg);
+ if (mExceptionStack != null) dest.writeString(mExceptionStack);
+ dest.writeInt(mErrorCode);
+ dest.writeInt(mSubErrorCode);
+ dest.writeLong(mTimeSincePlaybackCreatedMillis);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ /* package-private */ PlaybackErrorEvent(@NonNull Parcel in) {
+ byte flg = in.readByte();
+ String exceptionStack = (flg & 0x1) == 0 ? null : in.readString();
+ int errorCode = in.readInt();
+ int subErrorCode = in.readInt();
+ long timeSincePlaybackCreatedMillis = in.readLong();
+
+ this.mExceptionStack = exceptionStack;
+ this.mErrorCode = errorCode;
+ this.mSubErrorCode = subErrorCode;
+ this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+ }
+
+ public static final @NonNull Parcelable.Creator<PlaybackErrorEvent> CREATOR =
+ new Parcelable.Creator<PlaybackErrorEvent>() {
+ @Override
+ public PlaybackErrorEvent[] newArray(int size) {
+ return new PlaybackErrorEvent[size];
+ }
+
+ @Override
+ public PlaybackErrorEvent createFromParcel(@NonNull Parcel in) {
+ return new PlaybackErrorEvent(in);
+ }
+ };
+
+ /**
+ * A builder for {@link PlaybackErrorEvent}
+ */
+ public static final class Builder {
+ private @Nullable Exception mException;
+ private int mErrorCode;
+ private int mSubErrorCode;
+ private long mTimeSincePlaybackCreatedMillis;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @hide
+ */
+ public Builder(
+ @Nullable Exception exception,
+ int errorCode,
+ int subErrorCode,
+ long timeSincePlaybackCreatedMillis) {
+ mException = exception;
+ mErrorCode = errorCode;
+ mSubErrorCode = subErrorCode;
+ mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+ }
+
+ /**
+ * Sets the {@link Exception} object.
+ */
+ public @NonNull Builder setException(@NonNull Exception value) {
+ mException = value;
+ return this;
+ }
+
+ /**
+ * Sets error code.
+ */
+ public @NonNull Builder setErrorCode(@ErrorCode int value) {
+ mErrorCode = value;
+ return this;
+ }
+
+ /**
+ * Sets sub error code.
+ */
+ public @NonNull Builder setSubErrorCode(int value) {
+ mSubErrorCode = value;
+ return this;
+ }
+
+ /**
+ * Set the timestamp in milliseconds.
+ */
+ public @NonNull Builder setTimeSincePlaybackCreatedMillis(long value) {
+ mTimeSincePlaybackCreatedMillis = value;
+ return this;
+ }
+
+ /** Builds the instance. */
+ public @NonNull PlaybackErrorEvent build() {
+
+ String stack;
+ if (mException.getStackTrace() != null && mException.getStackTrace().length > 0) {
+ // TODO: a better definition of the stack trace
+ stack = mException.getStackTrace()[0].toString();
+ } else {
+ stack = null;
+ }
+
+ PlaybackErrorEvent o = new PlaybackErrorEvent(
+ stack,
+ mErrorCode,
+ mSubErrorCode,
+ mTimeSincePlaybackCreatedMillis);
+ return o;
+ }
+ }
+}
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 d51ff47..f48ffe7 100644
--- a/media/java/android/media/metrics/PlaybackMetricsManager.java
+++ b/media/java/android/media/metrics/PlaybackMetricsManager.java
@@ -48,6 +48,41 @@
throw e.rethrowFromSystemServer();
}
}
+ /**
+ * Reports network event.
+ * @hide
+ */
+ public void reportNetworkEvent(@NonNull String sessionId, NetworkEvent event) {
+ try {
+ mService.reportNetworkEvent(sessionId, event, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Reports playback state event.
+ * @hide
+ */
+ public void reportPlaybackStateEvent(@NonNull String sessionId, PlaybackStateEvent event) {
+ try {
+ mService.reportPlaybackStateEvent(sessionId, event, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Reports track change event.
+ * @hide
+ */
+ public void reportTrackChangeEvent(@NonNull String sessionId, TrackChangeEvent event) {
+ try {
+ mService.reportTrackChangeEvent(sessionId, event, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
/**
* Creates a playback session.
@@ -61,4 +96,16 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Reports error event.
+ * @hide
+ */
+ public void reportPlaybackErrorEvent(@NonNull String sessionId, PlaybackErrorEvent event) {
+ try {
+ mService.reportPlaybackErrorEvent(sessionId, event, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/media/java/android/media/metrics/PlaybackSession.java b/media/java/android/media/metrics/PlaybackSession.java
index 4ad8906..0a77516 100644
--- a/media/java/android/media/metrics/PlaybackSession.java
+++ b/media/java/android/media/metrics/PlaybackSession.java
@@ -50,6 +50,34 @@
mManager.reportPlaybackMetrics(mId, metrics);
}
+ /**
+ * Reports error event.
+ */
+ public void reportPlaybackErrorEvent(PlaybackErrorEvent event) {
+ mManager.reportPlaybackErrorEvent(mId, event);
+ }
+
+ /**
+ * Reports network event.
+ */
+ public void reportNetworkEvent(NetworkEvent event) {
+ mManager.reportNetworkEvent(mId, event);
+ }
+
+ /**
+ * Reports playback state event.
+ */
+ public void reportPlaybackStateEvent(PlaybackStateEvent event) {
+ mManager.reportPlaybackStateEvent(mId, event);
+ }
+
+ /**
+ * Reports track change event.
+ */
+ public void reportTrackChangeEvent(TrackChangeEvent event) {
+ mManager.reportTrackChangeEvent(mId, event);
+ }
+
public @NonNull String getId() {
return mId;
}
diff --git a/media/java/android/media/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/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index ed99fad..d8d1ba13 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -94,6 +94,8 @@
// For the recording session
void startRecording(in IBinder sessionToken, in Uri programUri, in Bundle params, int userId);
void stopRecording(in IBinder sessionToken, int userId);
+ void pauseRecording(in IBinder sessionToken, in Bundle params, int userId);
+ void resumeRecording(in IBinder sessionToken, in Bundle params, int userId);
// For TV input hardware binding
List<TvInputHardwareInfo> getHardwareList();
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 24b87d5..158cf21 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -58,4 +58,6 @@
// For the recording session
void startRecording(in Uri programUri, in Bundle params);
void stopRecording();
+ void pauseRecording(in Bundle params);
+ void resumeRecording(in Bundle params);
}
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index e89d33d..abccf8d 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -68,6 +68,8 @@
private static final int DO_TIME_SHIFT_ENABLE_POSITION_TRACKING = 19;
private static final int DO_START_RECORDING = 20;
private static final int DO_STOP_RECORDING = 21;
+ private static final int DO_PAUSE_RECORDING = 22;
+ private static final int DO_RESUME_RECORDING = 23;
private final boolean mIsRecordingSession;
private final HandlerCaller mCaller;
@@ -224,6 +226,14 @@
mTvInputRecordingSessionImpl.stopRecording();
break;
}
+ case DO_PAUSE_RECORDING: {
+ mTvInputRecordingSessionImpl.pauseRecording((Bundle) msg.obj);
+ break;
+ }
+ case DO_RESUME_RECORDING: {
+ mTvInputRecordingSessionImpl.resumeRecording((Bundle) msg.obj);
+ break;
+ }
default: {
Log.w(TAG, "Unhandled message code: " + msg.what);
break;
@@ -363,6 +373,16 @@
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_STOP_RECORDING));
}
+ @Override
+ public void pauseRecording(@Nullable Bundle params) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_PAUSE_RECORDING, params));
+ }
+
+ @Override
+ public void resumeRecording(@Nullable Bundle params) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RESUME_RECORDING, params));
+ }
+
private final class TvInputEventReceiver extends InputEventReceiver {
public TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
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/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 195ad5b..54cb2bf 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -143,6 +143,7 @@
// Attributes from XML meta data.
private final String mSetupActivity;
private final boolean mCanRecord;
+ private final boolean mCanPauseRecording;
private final int mTunerCount;
// Attributes specific to HDMI
@@ -264,8 +265,8 @@
private TvInputInfo(ResolveInfo service, String id, int type, boolean isHardwareInput,
CharSequence label, int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected,
- String setupActivity, boolean canRecord, int tunerCount, HdmiDeviceInfo hdmiDeviceInfo,
- boolean isConnectedToHdmiSwitch,
+ String setupActivity, boolean canRecord, boolean canPauseRecording, int tunerCount,
+ HdmiDeviceInfo hdmiDeviceInfo, boolean isConnectedToHdmiSwitch,
@HdmiAddressRelativePosition int hdmiConnectionRelativePosition, String parentId,
Bundle extras) {
mService = service;
@@ -279,6 +280,7 @@
mIconDisconnected = iconDisconnected;
mSetupActivity = setupActivity;
mCanRecord = canRecord;
+ mCanPauseRecording = canPauseRecording;
mTunerCount = tunerCount;
mHdmiDeviceInfo = hdmiDeviceInfo;
mIsConnectedToHdmiSwitch = isConnectedToHdmiSwitch;
@@ -386,6 +388,14 @@
}
/**
+ * Returns {@code true} if this TV input can pause recording TV programs,
+ * {@code false} otherwise.
+ */
+ public boolean canPauseRecording() {
+ return mCanPauseRecording;
+ }
+
+ /**
* Returns domain-specific extras associated with this TV input.
*/
public Bundle getExtras() {
@@ -571,6 +581,7 @@
&& Objects.equals(mIconDisconnected, obj.mIconDisconnected)
&& TextUtils.equals(mSetupActivity, obj.mSetupActivity)
&& mCanRecord == obj.mCanRecord
+ && mCanPauseRecording == obj.mCanPauseRecording
&& mTunerCount == obj.mTunerCount
&& Objects.equals(mHdmiDeviceInfo, obj.mHdmiDeviceInfo)
&& mIsConnectedToHdmiSwitch == obj.mIsConnectedToHdmiSwitch
@@ -606,6 +617,7 @@
dest.writeParcelable(mIconDisconnected, flags);
dest.writeString(mSetupActivity);
dest.writeByte(mCanRecord ? (byte) 1 : 0);
+ dest.writeByte(mCanPauseRecording ? (byte) 1 : 0);
dest.writeInt(mTunerCount);
dest.writeParcelable(mHdmiDeviceInfo, flags);
dest.writeByte(mIsConnectedToHdmiSwitch ? (byte) 1 : 0);
@@ -648,6 +660,7 @@
mIconDisconnected = in.readParcelable(null);
mSetupActivity = in.readString();
mCanRecord = in.readByte() == 1;
+ mCanPauseRecording = in.readByte() == 1;
mTunerCount = in.readInt();
mHdmiDeviceInfo = in.readParcelable(null);
mIsConnectedToHdmiSwitch = in.readByte() == 1;
@@ -695,6 +708,7 @@
private Icon mIconDisconnected;
private String mSetupActivity;
private Boolean mCanRecord;
+ private Boolean mCanPauseRecording;
private Integer mTunerCount;
private TvInputHardwareInfo mTvInputHardwareInfo;
private HdmiDeviceInfo mHdmiDeviceInfo;
@@ -879,6 +893,18 @@
}
/**
+ * Sets whether this TV input can pause recording TV programs or not.
+ *
+ * @param canPauseRecording Whether this TV input can pause recording TV programs.
+ * @return This Builder object to allow for chaining of calls to builder methods.
+ */
+ @NonNull
+ public Builder setCanPauseRecording(boolean canPauseRecording) {
+ this.mCanPauseRecording = canPauseRecording;
+ return this;
+ }
+
+ /**
* Sets domain-specific extras associated with this TV input.
*
* @param extras Domain-specific extras associated with this TV input. Keys <em>must</em> be
@@ -927,7 +953,9 @@
parseServiceMetadata(type);
return new TvInputInfo(mResolveInfo, id, type, isHardwareInput, mLabel, mLabelResId,
mIcon, mIconStandby, mIconDisconnected, mSetupActivity,
- mCanRecord == null ? false : mCanRecord, mTunerCount == null ? 0 : mTunerCount,
+ mCanRecord == null ? false : mCanRecord,
+ mCanPauseRecording == null ? false : mCanPauseRecording,
+ mTunerCount == null ? 0 : mTunerCount,
mHdmiDeviceInfo, isConnectedToHdmiSwitch, hdmiConnectionRelativePosition,
mParentId, mExtras);
}
@@ -997,6 +1025,12 @@
mTunerCount = sa.getInt(
com.android.internal.R.styleable.TvInputService_tunerCount, 1);
}
+ if (mCanPauseRecording == null) {
+ mCanPauseRecording = sa.getBoolean(
+ com.android.internal.R.styleable.TvInputService_canPauseRecording,
+ false);
+ }
+
sa.recycle();
} catch (IOException | XmlPullParserException e) {
throw new IllegalStateException("Failed reading meta-data for " + si.packageName, e);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index e9959be..98b9ad8 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -2523,6 +2523,40 @@
}
/**
+ * Pauses TV program recording in the current recording session.
+ *
+ * @param params A set of extra parameters which might be handled with this event.
+ */
+ void pauseRecording(@NonNull Bundle params) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.pauseRecording(mToken, params, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Resumes TV program recording in the current recording session.
+ *
+ * @param params A set of extra parameters which might be handled with this event.
+ */
+ void resumeRecording(@NonNull Bundle params) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.resumeRecording(mToken, params, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Calls {@link TvInputService.Session#appPrivateCommand(String, Bundle)
* TvInputService.Session.appPrivateCommand()} on the current TvView.
*
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 945fb3b..77fb2b2 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1873,6 +1873,28 @@
/**
+ * Called when the application requests to pause TV program recording. Recording must pause
+ * immediately when this method is called.
+ *
+ * If the pause request cannot be fulfilled, the session must call
+ * {@link #notifyError(int)}.
+ *
+ * @param params Domain-specific data for recording request.
+ */
+ public void onPauseRecording(@NonNull Bundle params) { }
+
+ /**
+ * Called when the application requests to resume TV program recording. Recording must
+ * resume immediately when this method is called.
+ *
+ * If the resume request cannot be fulfilled, the session must call
+ * {@link #notifyError(int)}.
+ *
+ * @param params Domain-specific data for recording request.
+ */
+ public void onResumeRecording(@NonNull Bundle params) { }
+
+ /**
* Called when the application requests to release all the resources held by this recording
* session.
*/
@@ -1924,6 +1946,22 @@
}
/**
+ * Calls {@link #onPauseRecording(Bundle)}.
+ *
+ */
+ void pauseRecording(@NonNull Bundle params) {
+ onPauseRecording(params);
+ }
+
+ /**
+ * Calls {@link #onResumeRecording(Bundle)}.
+ *
+ */
+ void resumeRecording(@NonNull Bundle params) {
+ onResumeRecording(params);
+ }
+
+ /**
* Calls {@link #onAppPrivateCommand(String, Bundle)}.
*/
void appPrivateCommand(String action, Bundle data) {
diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java
index 23fadac..180e2bd 100644
--- a/media/java/android/media/tv/TvRecordingClient.java
+++ b/media/java/android/media/tv/TvRecordingClient.java
@@ -30,6 +30,7 @@
import android.util.Pair;
import java.util.ArrayDeque;
+import java.util.Objects;
import java.util.Queue;
/**
@@ -49,6 +50,8 @@
private boolean mIsRecordingStarted;
private boolean mIsTuned;
+ private boolean mIsPaused;
+ private boolean mIsRecordingStopping;
private final Queue<Pair<String, Bundle>> mPendingAppPrivateCommands = new ArrayDeque<>();
/**
@@ -113,17 +116,22 @@
if (TextUtils.isEmpty(inputId)) {
throw new IllegalArgumentException("inputId cannot be null or an empty string");
}
- if (mIsRecordingStarted) {
+ if (mIsRecordingStarted && !mIsPaused) {
throw new IllegalStateException("tune failed - recording already started");
}
if (mSessionCallback != null && TextUtils.equals(mSessionCallback.mInputId, inputId)) {
if (mSession != null) {
+ mSessionCallback.mChannelUri = channelUri;
mSession.tune(channelUri, params);
} else {
mSessionCallback.mChannelUri = channelUri;
mSessionCallback.mConnectionParams = params;
}
+ mIsTuned = false;
} else {
+ if (mIsPaused) {
+ throw new IllegalStateException("tune failed - inputId is changed during pause");
+ }
resetInternal();
mSessionCallback = new MySessionCallback(inputId, channelUri, params);
if (mTvInputManager != null) {
@@ -148,6 +156,8 @@
mSession.release();
mIsTuned = false;
mIsRecordingStarted = false;
+ mIsPaused = false;
+ mIsRecordingStopping = false;
mSession = null;
}
}
@@ -169,7 +179,8 @@
*
* @param programUri The URI for the TV program to record, built by
* {@link TvContract#buildProgramUri(long)}. Can be {@code null}.
- * @throws IllegalStateException If {@link #tune} request hasn't been handled yet.
+ * @throws IllegalStateException If {@link #tune} request hasn't been handled yet or during
+ * pause.
*/
public void startRecording(@Nullable Uri programUri) {
startRecording(programUri, Bundle.EMPTY);
@@ -195,11 +206,16 @@
* @param params Domain-specific data for this request. Keys <em>must</em> be a scoped
* name, i.e. prefixed with a package name you own, so that different developers will
* not create conflicting keys.
- * @throws IllegalStateException If {@link #tune} request hasn't been handled yet.
+ * @throws IllegalStateException If {@link #tune} request hasn't been handled yet or during
+ * pause.
*/
public void startRecording(@Nullable Uri programUri, @NonNull Bundle params) {
- if (!mIsTuned) {
- throw new IllegalStateException("startRecording failed - not yet tuned");
+ if (mIsRecordingStopping || !mIsTuned || mIsPaused) {
+ throw new IllegalStateException("startRecording failed -"
+ + "recording not yet stopped or not yet tuned or paused");
+ }
+ if (mIsRecordingStarted) {
+ Log.w(TAG, "startRecording failed - recording already started");
}
if (mSession != null) {
mSession.startRecording(programUri, params);
@@ -225,6 +241,103 @@
}
if (mSession != null) {
mSession.stopRecording();
+ if (mIsRecordingStarted) {
+ mIsRecordingStopping = true;
+ }
+ }
+ }
+
+ /**
+ * Pause TV program recording in the current recording session. Recording is expected to pause
+ * immediately when this method is called. If recording has not yet started in the current
+ * recording session, this method does nothing.
+ *
+ * <p>In pause status, the application can tune during recording. To continue recording,
+ * please call {@link TvRecordingClient#resumeRecording()} to resume instead of
+ * {@link TvRecordingClient#startRecording(Uri)}. Application can stop
+ * the recording with {@link TvRecordingClient#stopRecording()} in recording pause status.
+ *
+ * <p>If the pause request cannot be fulfilled, the recording session will respond by calling
+ * {@link RecordingCallback#onError(int)}.
+ */
+ public void pauseRecording() {
+ pauseRecording(Bundle.EMPTY);
+ }
+
+ /**
+ * Pause TV program recording in the current recording session. Recording is expected to pause
+ * immediately when this method is called. If recording has not yet started in the current
+ * recording session, this method does nothing.
+ *
+ * <p>In pause status, the application can tune during recording. To continue recording,
+ * please call {@link TvRecordingClient#resumeRecording()} to resume instead of
+ * {@link TvRecordingClient#startRecording(Uri)}. Application can stop
+ * the recording with {@link TvRecordingClient#stopRecording()} in recording pause status.
+ *
+ * <p>If the pause request cannot be fulfilled, the recording session will respond by calling
+ * {@link RecordingCallback#onError(int)}.
+ *
+ * @param params Domain-specific data for this request.
+ */
+ public void pauseRecording(@NonNull Bundle params) {
+ if (!mIsRecordingStarted || mIsRecordingStopping) {
+ throw new IllegalStateException(
+ "pauseRecording failed - recording not yet started or stopping");
+ }
+ TvInputInfo info = mTvInputManager.getTvInputInfo(mSessionCallback.mInputId);
+ if (info == null || !info.canPauseRecording()) {
+ throw new UnsupportedOperationException(
+ "pauseRecording failed - operation not supported");
+ }
+ if (mIsPaused) {
+ Log.w(TAG, "pauseRecording failed - recording already paused");
+ }
+ if (mSession != null) {
+ mSession.pauseRecording(params);
+ mIsPaused = true;
+ }
+ }
+
+ /**
+ * Resume TV program recording only in recording pause status in the current recording session.
+ * Recording is expected to resume immediately when this method is called. If recording has not
+ * yet paused in the current recording session, this method does nothing.
+ *
+ * <p>When record is resumed, the recording is continue and can not re-tune. Application can
+ * stop the recording with {@link TvRecordingClient#stopRecording()} after record resumed.
+ *
+ * <p>If the pause request cannot be fulfilled, the recording session will respond by calling
+ * {@link RecordingCallback#onError(int)}.
+ */
+ public void resumeRecording() {
+ resumeRecording(Bundle.EMPTY);
+ }
+
+ /**
+ * Resume TV program recording only in recording pause status in the current recording session.
+ * Recording is expected to resume immediately when this method is called. If recording has not
+ * yet paused in the current recording session, this method does nothing.
+ *
+ * <p>When record is resumed, the recording is continues and can not re-tune. Application can
+ * stop the recording with {@link TvRecordingClient#stopRecording()} after record resumed.
+ *
+ * <p>If the resume request cannot be fulfilled, the recording session will respond by calling
+ * {@link RecordingCallback#onError(int)}.
+ *
+ * @param params Domain-specific data for this request.
+ */
+ public void resumeRecording(@NonNull Bundle params) {
+ if (!mIsRecordingStarted || mIsRecordingStopping || !mIsTuned) {
+ throw new IllegalStateException(
+ "resumeRecording failed - recording not yet started or stopping or "
+ + "not yet tuned");
+ }
+ if (!mIsPaused) {
+ Log.w(TAG, "resumeRecording failed - recording not yet paused");
+ }
+ if (mSession != null) {
+ mSession.resumeRecording(params);
+ mIsPaused = false;
}
}
@@ -367,6 +480,10 @@
Log.w(TAG, "onTuned - session not created");
return;
}
+ if (mIsTuned || !Objects.equals(mChannelUri, channelUri)) {
+ Log.w(TAG, "onTuned - already tuned or not yet tuned to last channel");
+ return;
+ }
mIsTuned = true;
mCallback.onTuned(channelUri);
}
@@ -382,6 +499,8 @@
}
mIsTuned = false;
mIsRecordingStarted = false;
+ mIsPaused = false;
+ mIsRecordingStopping = false;
mSessionCallback = null;
mSession = null;
if (mCallback != null) {
@@ -398,7 +517,13 @@
Log.w(TAG, "onRecordingStopped - session not created");
return;
}
+ if (!mIsRecordingStarted) {
+ Log.w(TAG, "onRecordingStopped - recording not yet started");
+ return;
+ }
mIsRecordingStarted = false;
+ mIsPaused = false;
+ mIsRecordingStopping = false;
mCallback.onRecordingStopped(recordedProgramUri);
}
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/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp
index 14761a6..d6d64f6 100644
--- a/media/jni/tuner/FrontendClient.cpp
+++ b/media/jni/tuner/FrontendClient.cpp
@@ -21,18 +21,39 @@
#include "FrontendClient.h"
+using ::aidl::android::media::tv::tuner::TunerFrontendScanAtsc3PlpInfo;
using ::aidl::android::media::tv::tuner::TunerFrontendSettings;
+
+using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard;
+using ::android::hardware::tv::tuner::V1_0::FrontendAnalogType;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtscModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Modulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbcAnnex;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbcModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbsModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbsStandard;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtStandard;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Modulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendScanAtsc3PlpInfo;
+using ::android::hardware::tv::tuner::V1_0::FrontendType;
using ::android::hardware::tv::tuner::V1_1::Constant;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbModulation;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbtConstellation;
+using ::android::hardware::tv::tuner::V1_1::FrontendModulation;
namespace android {
/////////////// FrontendClient ///////////////////////
-FrontendClient::FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int id) {
+FrontendClient::FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int id, int type) {
mTunerFrontend = tunerFrontend;
mAidlCallback = NULL;
mHidlCallback = NULL;
mId = id;
+ mType = type;
}
FrontendClient::~FrontendClient() {
@@ -42,11 +63,13 @@
mAidlCallback = NULL;
mHidlCallback = NULL;
mId = -1;
+ mType = -1;
}
Result FrontendClient::setCallback(sp<FrontendClientCallback> frontendClientCallback) {
if (mTunerFrontend != NULL) {
mAidlCallback = ::ndk::SharedRefBase::make<TunerFrontendCallback>(frontendClientCallback);
+ mAidlCallback->setFrontendType(mType);
mTunerFrontend->setCallback(mAidlCallback);
return Result::SUCCESS;
}
@@ -298,56 +321,21 @@
return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
}
-Status TunerFrontendCallback::onLocked() {
- return Status::ok();
-}
-
-Status TunerFrontendCallback::onScanStopped() {
- return Status::ok();
-}
-
-Status TunerFrontendCallback::onProgress(int /*percent*/) {
- return Status::ok();
-}
-
-Status TunerFrontendCallback::onFrequenciesReport(const vector<int>& /*frequency*/) {
- return Status::ok();
-}
-
-Status TunerFrontendCallback::onSymbolRates(const vector<int>& /*rates*/) {
- return Status::ok();
-}
-
-Status TunerFrontendCallback::onHierarchy(int /*hierarchy*/) {
- return Status::ok();
-}
-
-Status TunerFrontendCallback::onSignalType(int /*signalType*/) {
- return Status::ok();
-}
-
-Status TunerFrontendCallback::onPlpIds(const vector<int>& /*plpIds*/) {
- return Status::ok();
-}
-
-Status TunerFrontendCallback::onGroupIds(const vector<int>& /*groupIds*/) {
- return Status::ok();
-}
-
-Status TunerFrontendCallback::onInputStreamIds(const vector<int>& /*inputStreamIds*/) {
- return Status::ok();
-}
-
-Status TunerFrontendCallback::onDvbsStandard(int /*dvbsStandandard*/) {
- return Status::ok();
-}
-
-Status TunerFrontendCallback::onAnalogSifStandard(int /*sifStandandard*/) {
- return Status::ok();
-}
-
-Status TunerFrontendCallback::onAtsc3PlpInfos(const vector<TunerAtsc3PlpInfo>& /*atsc3PlpInfos*/) {
- return Status::ok();
+Status TunerFrontendCallback::onScanMessage(int messageType,
+ const TunerFrontendScanMessage& message) {
+ if (mFrontendClientCallback != NULL) {
+ if (!is1_1ExtendedScanMessage(messageType)) {
+ mFrontendClientCallback->onScanMessage(
+ static_cast<FrontendScanMessageType>(messageType),
+ getHalScanMessage(messageType, message));
+ } else {
+ mFrontendClientCallback->onScanMessageExt1_1(
+ static_cast<FrontendScanMessageTypeExt1_1>(messageType),
+ getHalScanMessageExt1_1(messageType, message));
+ }
+ return Status::ok();
+ }
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
}
/////////////// IFrontendCallback ///////////////////////
@@ -377,4 +365,167 @@
}
return Void();
}
+
+/////////////// FrontendClientCallback Helper Methods ///////////////////////
+
+FrontendScanMessage TunerFrontendCallback::getHalScanMessage(
+ int messageType, const TunerFrontendScanMessage& message) {
+ FrontendScanMessage scanMessage;
+ switch (messageType) {
+ case (int) FrontendScanMessageType::LOCKED:
+ scanMessage.isLocked(message.get<TunerFrontendScanMessage::isLocked>());
+ break;
+ case (int) FrontendScanMessageType::END:
+ scanMessage.isEnd(message.get<TunerFrontendScanMessage::isEnd>());
+ break;
+ case (int) FrontendScanMessageType::PROGRESS_PERCENT:
+ scanMessage.progressPercent(message.get<TunerFrontendScanMessage::progressPercent>());
+ break;
+ case (int) FrontendScanMessageType::FREQUENCY: {
+ vector<int> f = message.get<TunerFrontendScanMessage::frequencies>();
+ hidl_vec<uint32_t> frequencies(begin(f), end(f));
+ scanMessage.frequencies(frequencies);
+ break;
+ }
+ case (int) FrontendScanMessageType::SYMBOL_RATE: {
+ vector<int> s = message.get<TunerFrontendScanMessage::symbolRates>();
+ hidl_vec<uint32_t> symbolRates(begin(s), end(s));
+ scanMessage.symbolRates(symbolRates);
+ break;
+ }
+ case (int) FrontendScanMessageType::HIERARCHY:
+ scanMessage.hierarchy(static_cast<FrontendDvbtHierarchy>(
+ message.get<TunerFrontendScanMessage::hierarchy>()));
+ break;
+ case (int) FrontendScanMessageType::ANALOG_TYPE:
+ scanMessage.analogType(static_cast<FrontendAnalogType>(
+ message.get<TunerFrontendScanMessage::analogType>()));
+ break;
+ case (int) FrontendScanMessageType::PLP_IDS: {
+ vector<uint8_t> p = message.get<TunerFrontendScanMessage::plpIds>();
+ hidl_vec<uint8_t> plpIds(begin(p), end(p));
+ scanMessage.plpIds(plpIds);
+ break;
+ }
+ case (int) FrontendScanMessageType::GROUP_IDS: {
+ vector<uint8_t> g = message.get<TunerFrontendScanMessage::groupIds>();
+ hidl_vec<uint8_t> groupIds(begin(g), end(g));
+ scanMessage.groupIds(groupIds);
+ break;
+ }
+ case (int) FrontendScanMessageType::INPUT_STREAM_IDS: {
+ vector<char16_t> i = message.get<TunerFrontendScanMessage::inputStreamIds>();
+ hidl_vec<uint16_t> inputStreamIds(begin(i), end(i));
+ scanMessage.inputStreamIds(inputStreamIds);
+ break;
+ }
+ case (int) FrontendScanMessageType::STANDARD: {
+ FrontendScanMessage::Standard std;
+ int standard = message.get<TunerFrontendScanMessage::std>();
+ switch (mType) {
+ case (int) FrontendType::DVBS:
+ std.sStd(static_cast<FrontendDvbsStandard>(standard));
+ scanMessage.std(std);
+ break;
+ case (int) FrontendType::DVBT:
+ std.tStd(static_cast<FrontendDvbtStandard>(standard));
+ scanMessage.std(std);
+ break;
+ case (int) FrontendType::ANALOG:
+ std.sifStd(static_cast<FrontendAnalogSifStandard>(standard));
+ scanMessage.std(std);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case (int) FrontendScanMessageType::ATSC3_PLP_INFO: {
+ vector<TunerFrontendScanAtsc3PlpInfo> plp =
+ message.get<TunerFrontendScanMessage::atsc3PlpInfos>();
+ hidl_vec<FrontendScanAtsc3PlpInfo> plpInfo;
+ for (TunerFrontendScanAtsc3PlpInfo info : plp) {
+ FrontendScanAtsc3PlpInfo p{
+ .plpId = static_cast<uint8_t>(info.plpId),
+ .bLlsFlag = info.llsFlag,
+ };
+ int size = plpInfo.size();
+ plpInfo.resize(size + 1);
+ plpInfo[size] = p;
+ }
+ scanMessage.atsc3PlpInfos(plpInfo);
+ break;
+ }
+ default:
+ break;
+ }
+ return scanMessage;
+}
+
+FrontendScanMessageExt1_1 TunerFrontendCallback::getHalScanMessageExt1_1(
+ int messageType, const TunerFrontendScanMessage& message) {
+ FrontendScanMessageExt1_1 scanMessage;
+ switch (messageType) {
+ case (int) FrontendScanMessageTypeExt1_1::HIGH_PRIORITY:
+ scanMessage.isHighPriority(message.get<TunerFrontendScanMessage::isHighPriority>());
+ break;
+ case (int) FrontendScanMessageTypeExt1_1::DVBC_ANNEX:
+ scanMessage.annex(static_cast<FrontendDvbcAnnex>(
+ message.get<TunerFrontendScanMessage::annex>()));
+ break;
+ case (int) FrontendScanMessageTypeExt1_1::MODULATION: {
+ FrontendModulation m;
+ int modulation = message.get<TunerFrontendScanMessage::modulation>();
+ switch (mType) {
+ case (int) FrontendType::DVBC:
+ m.dvbc(static_cast<FrontendDvbcModulation>(modulation));
+ scanMessage.modulation(m);
+ break;
+ case (int) FrontendType::DVBS:
+ m.dvbs(static_cast<FrontendDvbsModulation>(modulation));
+ scanMessage.modulation(m);
+ break;
+ case (int) FrontendType::DVBT:
+ m.dvbt(static_cast<FrontendDvbtConstellation>(modulation));
+ scanMessage.modulation(m);
+ break;
+ case (int) FrontendType::ISDBS:
+ m.isdbs(static_cast<FrontendIsdbsModulation>(modulation));
+ scanMessage.modulation(m);
+ break;
+ case (int) FrontendType::ISDBS3:
+ m.isdbs3(static_cast<FrontendIsdbs3Modulation>(modulation));
+ scanMessage.modulation(m);
+ break;
+ case (int) FrontendType::ISDBT:
+ m.isdbt(static_cast<FrontendIsdbtModulation>(modulation));
+ scanMessage.modulation(m);
+ break;
+ case (int) FrontendType::ATSC:
+ m.atsc(static_cast<FrontendAtscModulation>(modulation));
+ scanMessage.modulation(m);
+ break;
+ case (int) FrontendType::ATSC3:
+ m.atsc3(static_cast<FrontendAtsc3Modulation>(modulation));
+ scanMessage.modulation(m);
+ break;
+ case (int) hardware::tv::tuner::V1_1::FrontendType::DTMB:
+ m.dtmb(static_cast<FrontendDtmbModulation>(modulation));
+ scanMessage.modulation(m);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return scanMessage;
+}
+
+bool TunerFrontendCallback::is1_1ExtendedScanMessage(int messageType) {
+ return messageType >= (int)FrontendScanMessageTypeExt1_1::MODULATION
+ && messageType <= (int)FrontendScanMessageTypeExt1_1::HIGH_PRIORITY;
+}
} // namespace android
diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h
index 32356744..4f95c22 100644
--- a/media/jni/tuner/FrontendClient.h
+++ b/media/jni/tuner/FrontendClient.h
@@ -30,7 +30,7 @@
using ::aidl::android::media::tv::tuner::BnTunerFrontendCallback;
using ::aidl::android::media::tv::tuner::ITunerFrontend;
-using ::aidl::android::media::tv::tuner::TunerAtsc3PlpInfo;
+using ::aidl::android::media::tv::tuner::TunerFrontendScanMessage;
using ::android::hardware::Return;
using ::android::hardware::Void;
@@ -63,34 +63,18 @@
Status onEvent(int frontendEventType);
- Status onLocked();
+ Status onScanMessage(int messageType, const TunerFrontendScanMessage& message);
- Status onScanStopped();
-
- Status onProgress(int percent);
-
- Status onFrequenciesReport(const vector<int>& frequency);
-
- Status onSymbolRates(const vector<int>& rates);
-
- Status onHierarchy(int hierarchy);
-
- Status onSignalType(int signalType);
-
- Status onPlpIds(const vector<int>& plpIds);
-
- Status onGroupIds(const vector<int>& groupIds);
-
- Status onInputStreamIds(const vector<int>& inputStreamIds);
-
- Status onDvbsStandard(int dvbsStandandard);
-
- Status onAnalogSifStandard(int sifStandandard);
-
- Status onAtsc3PlpInfos(const vector<TunerAtsc3PlpInfo>& atsc3PlpInfos);
+ void setFrontendType(int frontendType) { mType = frontendType; }
private:
+ FrontendScanMessage getHalScanMessage(int messageType, const TunerFrontendScanMessage& message);
+ FrontendScanMessageExt1_1 getHalScanMessageExt1_1(int messageType,
+ const TunerFrontendScanMessage& message);
+ bool is1_1ExtendedScanMessage(int messageType);
+
sp<FrontendClientCallback> mFrontendClientCallback;
+ int mType;
};
struct HidlFrontendCallback : public IFrontendCallback {
@@ -111,7 +95,7 @@
struct FrontendClient : public RefBase {
public:
- FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int id);
+ FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int id, int type);
~FrontendClient();
/**
@@ -210,6 +194,7 @@
sp<HidlFrontendCallback> mHidlCallback;
int mId;
+ int mType;
};
} // namespace android
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index 39e6ba2..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;
@@ -98,14 +96,28 @@
// TODO: handle error code
shared_ptr<ITunerFrontend> tunerFrontend;
mTunerService->openFrontend(frontendHandle, &tunerFrontend);
- return new FrontendClient(tunerFrontend, frontendHandle);
+ if (tunerFrontend == NULL) {
+ return NULL;
+ }
+ int id;
+ // TODO: handle error code
+ tunerFrontend->getFrontendId(&id);
+ TunerFrontendInfo aidlFrontendInfo;
+ // TODO: handle error code
+ mTunerService->getFrontendInfo(id, &aidlFrontendInfo);
+ return new FrontendClient(tunerFrontend, frontendHandle, aidlFrontendInfo.type);
}
if (mTuner != NULL) {
int id = getResourceIdFromHandle(frontendHandle, FRONTEND);
sp<IFrontend> hidlFrontend = openHidlFrontendById(id);
if (hidlFrontend != NULL) {
- sp<FrontendClient> frontendClient = new FrontendClient(NULL, id);
+ FrontendInfo hidlInfo;
+ Result res = getHidlFrontendInfo(id, hidlInfo);
+ if (res != Result::SUCCESS) {
+ return NULL;
+ }
+ sp<FrontendClient> frontendClient = new FrontendClient(NULL, id, (int)hidlInfo.type);
frontendClient->setHidlFrontend(hidlFrontend);
return frontendClient;
}
@@ -116,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));
@@ -289,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);
@@ -438,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/android/include_platform/android/activity_manager.h b/native/android/include_platform/android/activity_manager.h
index 0ecd56d..aa86c74 100644
--- a/native/android/include_platform/android/activity_manager.h
+++ b/native/android/include_platform/android/activity_manager.h
@@ -115,8 +115,6 @@
AACTIVITYMANAGER_IMPORTANCE_GONE = 1000,
};
-#if __ANDROID_API__ >= 31
-
/**
* Adds a UidImportanceListener to the ActivityManager.
*
@@ -169,8 +167,6 @@
*/
int32_t AActivityManager_getUidImportance(uid_t uid) __INTRODUCED_IN(31);
-#endif // __ANDROID_API__ >= 31
-
__END_DECLS
#endif // __AACTIVITYMANAGER_H__
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 15b473c..3751564 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -22,22 +22,20 @@
"-Wunreachable-code",
],
- // our source files
- //
srcs: [
- "aassetstreamadaptor.cpp",
- "bitmap.cpp",
"imagedecoder.cpp",
],
shared_libs: [
- "libandroid",
"libandroid_runtime",
"libhwui",
"liblog",
],
- header_libs: [ "libhwui_internal_headers" ],
+ header_libs: [
+ "libhwui_internal_headers",
+ "jni_headers",
+ ],
static_libs: ["libarect"],
@@ -48,7 +46,24 @@
ldflags: ["-Wl,--hash-style=both"],
},
},
- version_script: "libjnigraphics.map.txt",
+ host_supported: true,
+ target: {
+ android: {
+ srcs: [
+ "aassetstreamadaptor.cpp",
+ "bitmap.cpp",
+ ],
+ shared_libs: [
+ "libandroid",
+ ],
+ version_script: "libjnigraphics.map.txt",
+ },
+ host: {
+ header_libs: [
+ "libnativewindow_headers",
+ ],
+ },
+ },
}
// The headers module is in frameworks/native/Android.bp.
@@ -58,3 +73,30 @@
first_version: "9",
unversioned_until: "current",
}
+
+cc_fuzz {
+ name: "imagedecoder_fuzzer",
+ srcs: ["fuzz_imagedecoder.cpp"],
+ header_libs: ["jni_headers"],
+ shared_libs: [
+ "libbinder",
+ "libjnigraphics",
+ "libutils",
+ ],
+ static_libs: ["libarect"],
+ fuzz_config: {
+ cc: ["scroggo@google.com"],
+ asan_options: [
+ "detect_odr_violation=1",
+ ],
+ hwasan_options: [
+ // Image decoders may attempt to allocate a large amount of memory
+ // (especially if the encoded image is large). This doesn't
+ // necessarily mean there is a bug. Set allocator_may_return_null=1
+ // for hwasan so the fuzzer can continue running.
+ "allocator_may_return_null = 1",
+ ],
+ },
+ corpus: ["corpus/*"],
+ host_supported: true,
+}
diff --git a/native/graphics/jni/corpus/baseline_jpeg.jpg b/native/graphics/jni/corpus/baseline_jpeg.jpg
new file mode 100644
index 0000000..ed5251c
--- /dev/null
+++ b/native/graphics/jni/corpus/baseline_jpeg.jpg
Binary files differ
diff --git a/native/graphics/jni/corpus/color_wheel.ico b/native/graphics/jni/corpus/color_wheel.ico
new file mode 100644
index 0000000..fdfa381c
--- /dev/null
+++ b/native/graphics/jni/corpus/color_wheel.ico
Binary files differ
diff --git a/native/graphics/jni/corpus/gif_test.gif b/native/graphics/jni/corpus/gif_test.gif
new file mode 100644
index 0000000..d1c2815
--- /dev/null
+++ b/native/graphics/jni/corpus/gif_test.gif
Binary files differ
diff --git a/native/graphics/jni/corpus/google_chrome.ico b/native/graphics/jni/corpus/google_chrome.ico
new file mode 100644
index 0000000..7af91ee
--- /dev/null
+++ b/native/graphics/jni/corpus/google_chrome.ico
Binary files differ
diff --git a/native/graphics/jni/corpus/heifwriter_input.heic b/native/graphics/jni/corpus/heifwriter_input.heic
new file mode 100644
index 0000000..1f4573a
--- /dev/null
+++ b/native/graphics/jni/corpus/heifwriter_input.heic
Binary files differ
diff --git a/native/graphics/jni/corpus/mandrill.wbmp b/native/graphics/jni/corpus/mandrill.wbmp
new file mode 100644
index 0000000..ac84598
--- /dev/null
+++ b/native/graphics/jni/corpus/mandrill.wbmp
Binary files differ
diff --git a/native/graphics/jni/corpus/png_test.png b/native/graphics/jni/corpus/png_test.png
new file mode 100644
index 0000000..5230051
--- /dev/null
+++ b/native/graphics/jni/corpus/png_test.png
Binary files differ
diff --git a/native/graphics/jni/corpus/progressive_jpeg.jpg b/native/graphics/jni/corpus/progressive_jpeg.jpg
new file mode 100644
index 0000000..6b58be4
--- /dev/null
+++ b/native/graphics/jni/corpus/progressive_jpeg.jpg
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_1mp.dng b/native/graphics/jni/corpus/sample_1mp.dng
new file mode 100644
index 0000000..c1c1078
--- /dev/null
+++ b/native/graphics/jni/corpus/sample_1mp.dng
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_arw.arw b/native/graphics/jni/corpus/sample_arw.arw
new file mode 100644
index 0000000..2b05931
--- /dev/null
+++ b/native/graphics/jni/corpus/sample_arw.arw
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_cr2.cr2 b/native/graphics/jni/corpus/sample_cr2.cr2
new file mode 100644
index 0000000..adbbc97
--- /dev/null
+++ b/native/graphics/jni/corpus/sample_cr2.cr2
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_nef.nef b/native/graphics/jni/corpus/sample_nef.nef
new file mode 100644
index 0000000..282614b
--- /dev/null
+++ b/native/graphics/jni/corpus/sample_nef.nef
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_nrw.nrw b/native/graphics/jni/corpus/sample_nrw.nrw
new file mode 100644
index 0000000..f91eff4
--- /dev/null
+++ b/native/graphics/jni/corpus/sample_nrw.nrw
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_orf.orf b/native/graphics/jni/corpus/sample_orf.orf
new file mode 100644
index 0000000..60eea3a
--- /dev/null
+++ b/native/graphics/jni/corpus/sample_orf.orf
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_pef.pef b/native/graphics/jni/corpus/sample_pef.pef
new file mode 100644
index 0000000..d4f6d48
--- /dev/null
+++ b/native/graphics/jni/corpus/sample_pef.pef
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_raf.raf b/native/graphics/jni/corpus/sample_raf.raf
new file mode 100644
index 0000000..edb23b4
--- /dev/null
+++ b/native/graphics/jni/corpus/sample_raf.raf
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_rw2.rw2 b/native/graphics/jni/corpus/sample_rw2.rw2
new file mode 100644
index 0000000..9db5b45
--- /dev/null
+++ b/native/graphics/jni/corpus/sample_rw2.rw2
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_srw.srw b/native/graphics/jni/corpus/sample_srw.srw
new file mode 100644
index 0000000..cb9b033
--- /dev/null
+++ b/native/graphics/jni/corpus/sample_srw.srw
Binary files differ
diff --git a/native/graphics/jni/corpus/webp-color-profile-lossless.webp b/native/graphics/jni/corpus/webp-color-profile-lossless.webp
new file mode 100644
index 0000000..4fd63d5
--- /dev/null
+++ b/native/graphics/jni/corpus/webp-color-profile-lossless.webp
Binary files differ
diff --git a/native/graphics/jni/corpus/webp_animated.webp b/native/graphics/jni/corpus/webp_animated.webp
new file mode 100644
index 0000000..35a8dfc
--- /dev/null
+++ b/native/graphics/jni/corpus/webp_animated.webp
Binary files differ
diff --git a/native/graphics/jni/corpus/webp_test.webp b/native/graphics/jni/corpus/webp_test.webp
new file mode 100644
index 0000000..7b1009f
--- /dev/null
+++ b/native/graphics/jni/corpus/webp_test.webp
Binary files differ
diff --git a/native/graphics/jni/fuzz_imagedecoder.cpp b/native/graphics/jni/fuzz_imagedecoder.cpp
new file mode 100644
index 0000000..015aca7
--- /dev/null
+++ b/native/graphics/jni/fuzz_imagedecoder.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 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.
+ */
+
+#include <android/imagedecoder.h>
+
+#include <binder/IPCThreadState.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <cstdlib>
+#include <memory>
+
+struct DecoderDeleter {
+ void operator()(AImageDecoder* decoder) const { AImageDecoder_delete(decoder); }
+};
+
+using DecoderPointer = std::unique_ptr<AImageDecoder, DecoderDeleter>;
+
+static DecoderPointer makeDecoder(const uint8_t* data, size_t size) {
+ AImageDecoder* decoder = nullptr;
+ int result = AImageDecoder_createFromBuffer(data, size, &decoder);
+ if (result != ANDROID_IMAGE_DECODER_SUCCESS) {
+ // This was not a valid image.
+ return nullptr;
+ }
+ return DecoderPointer(decoder);
+}
+
+struct PixelFreer {
+ void operator()(void* pixels) const { std::free(pixels); }
+};
+
+using PixelPointer = std::unique_ptr<void, PixelFreer>;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ // Without this call, decoding HEIF may time out on binder IPC calls.
+ android::ProcessState::self()->startThreadPool();
+
+ DecoderPointer decoder = makeDecoder(data, size);
+ if (!decoder) {
+ return 0;
+ }
+
+ const AImageDecoderHeaderInfo* info = AImageDecoder_getHeaderInfo(decoder.get());
+ int32_t width = AImageDecoderHeaderInfo_getWidth(info);
+ int32_t height = AImageDecoderHeaderInfo_getHeight(info);
+
+ // Set an arbitrary limit on the size of an image. The fuzzer runs with a
+ // limited amount of memory, and keeping this allocation small allows the
+ // fuzzer to continue running to try to find more serious problems. This
+ // size is large enough to hold a photo taken by a current gen phone.
+ constexpr int32_t kMaxDimension = 5000;
+ if (width > kMaxDimension || height > kMaxDimension) {
+ return 0;
+ }
+
+ size_t stride = AImageDecoder_getMinimumStride(decoder.get());
+ size_t pixelSize = height * stride;
+ auto pixels = PixelPointer(std::malloc(pixelSize));
+ if (!pixels.get()) {
+ return 0;
+ }
+
+ while (true) {
+ int result = AImageDecoder_decodeImage(decoder.get(), pixels.get(), stride, pixelSize);
+ if (result != ANDROID_IMAGE_DECODER_SUCCESS) break;
+
+ result = AImageDecoder_advanceFrame(decoder.get());
+ if (result != ANDROID_IMAGE_DECODER_SUCCESS) break;
+ }
+ return 0;
+}
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index 5973790..385e455 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -121,8 +121,12 @@
}
*outDecoder = nullptr;
+#ifdef __ANDROID__
auto stream = std::make_unique<AAssetStreamAdaptor>(asset);
return createFromStream(std::move(stream), outDecoder);
+#else
+ return ANDROID_IMAGE_DECODER_INTERNAL_ERROR;
+#endif
}
static bool isSeekable(int descriptor) {
@@ -349,7 +353,7 @@
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 8fc3531..cdf4851 100644
--- a/packages/CompanionDeviceManager/res/values-af/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-af/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Jy het <xliff:g id="APP_NAME_0">%1$s</xliff:g> nodig om jou <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> te bestuur. <xliff:g id="APP_NAME2">%3$s</xliff:g> sal toegang hê tot <xliff:g id="PERMISSIONS">%4$s</xliff:g> terwyl die <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> gekoppel is."</string>
+ <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 ff31454..a03ea0df 100644
--- a/packages/CompanionDeviceManager/res/values-am/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-am/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> የእርስዎን <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> ለማስተዳደር ያስፈልጋል። <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> ተገናኝቶ ሳለ <xliff:g id="APP_NAME2">%3$s</xliff:g> የ<xliff:g id="PERMISSIONS">%4$s</xliff:g> መዳረሻን ያገኛል።"</string>
+ <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 aedf0f3..970c46b 100644
--- a/packages/CompanionDeviceManager/res/values-ar/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"يجب توفّر تطبيق <xliff:g id="APP_NAME_0">%1$s</xliff:g> لإدارة <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. سيتمكن تطبيق <xliff:g id="APP_NAME2">%3$s</xliff:g> من الوصول إلى <xliff:g id="PERMISSIONS">%4$s</xliff:g> عندما يكون <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> مرتبطًا."</string>
+ <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 f0c3ee8..477844c 100644
--- a/packages/CompanionDeviceManager/res/values-as/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-as/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"আপোনাৰ <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> পৰিচালনা কৰিবলৈ <xliff:g id="APP_NAME_0">%1$s</xliff:g>ৰ আৱশ্যক। <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>ৰ সৈতে সংযোগ কৰিলে <xliff:g id="APP_NAME2">%3$s</xliff:g>এ <xliff:g id="PERMISSIONS">%4$s</xliff:g>লৈ এক্সেছ পাব।"</string>
+ <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 64bea4d..f10c639 100644
--- a/packages/CompanionDeviceManager/res/values-az/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-az/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> profilinizi idarə etmək üçün <xliff:g id="APP_NAME_0">%1$s</xliff:g> tələb olunur. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> qoşulu olduqda <xliff:g id="APP_NAME2">%3$s</xliff:g> <xliff:g id="PERMISSIONS">%4$s</xliff:g> bölməsinə giriş əldə edəcək."</string>
+ <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 3f06722..e8542f3 100644
--- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> je neophodna za upravljanje profilom <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> će dobiti pristup dozvolama za <xliff:g id="PERMISSIONS">%4$s</xliff:g> dok je <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> povezan."</string>
+ <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 25e235c..13be6f2 100644
--- a/packages/CompanionDeviceManager/res/values-be/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-be/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Для кіравання прыладай \"<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>\" патрабуецца праграма \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\". Калі прылада \"<xliff:g id="PROFILE_NAME2">%5$s</xliff:g>\" будзе падключана, <xliff:g id="APP_NAME2">%3$s</xliff:g> атрымае наступныя дазволы: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+ <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 264ce27..3bda5e6 100644
--- a/packages/CompanionDeviceManager/res/values-bg/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"За управление на <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> се изисква <xliff:g id="APP_NAME_0">%1$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> ще получи достъп до <xliff:g id="PERMISSIONS">%4$s</xliff:g>, докато устройството (<xliff:g id="PROFILE_NAME2">%5$s</xliff:g>) е свързано."</string>
+ <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 65f92c9..d3bc515 100644
--- a/packages/CompanionDeviceManager/res/values-bn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>-কে আপনার <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>.ম্যানেজ করতে দিতে হবে। <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> কানেক্ট করা হলে <xliff:g id="APP_NAME2">%3$s</xliff:g> <xliff:g id="PERMISSIONS">%4$s</xliff:g> অ্যাক্সেস করতে পারবে।"</string>
+ <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 f8e24b7..905b306 100644
--- a/packages/CompanionDeviceManager/res/values-bs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Potrebna je aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> za upravljanje uređajem <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. Aplikacija <xliff:g id="APP_NAME2">%3$s</xliff:g> će dobiti pristup uslugama <xliff:g id="PERMISSIONS">%4$s</xliff:g> dok je uređaj <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> povezan."</string>
+ <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 ae8ca9f..86dc694 100644
--- a/packages/CompanionDeviceManager/res/values-ca/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Cal <xliff:g id="APP_NAME_0">%1$s</xliff:g> per gestionar el teu <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> tindrà accés als permisos <xliff:g id="PERMISSIONS">%4$s</xliff:g> mentre el <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> estigui connectat."</string>
+ <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-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml
index 675bd29..389ccd0 100644
--- a/packages/CompanionDeviceManager/res/values-cs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml
@@ -19,8 +19,9 @@
<string name="app_label" msgid="4470785958457506021">"Správce doprovodných zařízení"</string>
<string name="chooser_title" msgid="2262294130493605839">"Vyberte zařízení <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, které chcete spravovat pomocí aplikace <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"zařízení"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
<string name="confirmation_title" msgid="4751119145078041732">"Nastavit aplikaci <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ke správě tohoto zařízení: <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="3167701603666642104">"Ke správě zařízení <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> je potřeba aplikace <xliff:g id="APP_NAME_0">%1$s</xliff:g>. Dokud bude zařízení <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> připojeno, bude mít aplikace <xliff:g id="APP_NAME2">%3$s</xliff:g> přístup k těmto oprávněním: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+ <string name="profile_summary" msgid="2009764182871566255">"Ke správě profilu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> je potřeba aplikace <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">"Ano"</string>
<string name="consent_no" msgid="1335543792857823917">"Ne, díky"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml
index a6720fc..5a31f9b 100644
--- a/packages/CompanionDeviceManager/res/values-da/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-da/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> er nødvendig for at administrere: <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> får adgang til <xliff:g id="PERMISSIONS">%4$s</xliff:g>, mens <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> er forbundet."</string>
+ <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-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml
index eb2631f..ead68e3 100644
--- a/packages/CompanionDeviceManager/res/values-de/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -19,8 +19,11 @@
<string name="app_label" msgid="4470785958457506021">"Begleitgerät-Manager"</string>
<string name="chooser_title" msgid="2262294130493605839">"Gerät (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) auswählen, das von <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> verwaltet werden soll"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"Gerät"</string>
+ <!-- no translation found for profile_name_watch (576290739483672360) -->
+ <skip />
<string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> zum Verwalten deines Geräts (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) festlegen – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ist erforderlich, um dein Gerät (<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>) zu verwalten. <xliff:g id="APP_NAME2">%3$s</xliff:g> erhält Zugriff auf <xliff:g id="PERMISSIONS">%4$s</xliff:g>, während eine Verbindung mit <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> besteht."</string>
+ <!-- no translation found for profile_summary (2009764182871566255) -->
+ <skip />
<string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
<string name="consent_no" msgid="1335543792857823917">"Nein danke"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml
index cb31866..60de2ff 100644
--- a/packages/CompanionDeviceManager/res/values-el/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-el/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Η εφαρμογή <xliff:g id="APP_NAME_0">%1$s</xliff:g> απαιτείται για τη διαχείριση του προφίλ <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. Η εφαρμογή <xliff:g id="APP_NAME2">%3$s</xliff:g> θα έχει πρόσβαση στις άδειες <xliff:g id="PERMISSIONS">%4$s</xliff:g> ενώ είναι συνδεδεμένο το προφίλ <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>."</string>
+ <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 a30c199..2fed1ae 100644
--- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> will get access to <xliff:g id="PERMISSIONS">%4$s</xliff:g> while the <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> is connected."</string>
+ <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 a30c199..2fed1ae 100644
--- a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> will get access to <xliff:g id="PERMISSIONS">%4$s</xliff:g> while the <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> is connected."</string>
+ <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 a30c199..2fed1ae 100644
--- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> will get access to <xliff:g id="PERMISSIONS">%4$s</xliff:g> while the <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> is connected."</string>
+ <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 a30c199..2fed1ae 100644
--- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> will get access to <xliff:g id="PERMISSIONS">%4$s</xliff:g> while the <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> is connected."</string>
+ <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-rXC/strings.xml b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
index ea39d0a..f3c4b1d 100644
--- a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> will get access to <xliff:g id="PERMISSIONS">%4$s</xliff:g> while the <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> is connected."</string>
+ <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 63097dc..4fbb57e 100644
--- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Se requiere <xliff:g id="APP_NAME_0">%1$s</xliff:g> para administrar tu <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> accederá a <xliff:g id="PERMISSIONS">%4$s</xliff:g> mientras tu <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> esté conectado."</string>
+ <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 784c30d..5ca9305 100644
--- a/packages/CompanionDeviceManager/res/values-es/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Se necesita <xliff:g id="APP_NAME_0">%1$s</xliff:g> para gestionar tu <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. Mientras tu <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> esté conectado, <xliff:g id="APP_NAME2">%3$s</xliff:g> tendrá acceso a lo siguiente: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+ <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 b505870..357f052 100644
--- a/packages/CompanionDeviceManager/res/values-et/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-et/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> on vajalik teie seadme <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> haldamiseks. <xliff:g id="APP_NAME2">%3$s</xliff:g> saab juurdepääsuload (<xliff:g id="PERMISSIONS">%4$s</xliff:g>), kui seade <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> on ühendatud."</string>
+ <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 826556d..14c7154 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> erabili behar duzu <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> kudeatzeko. <xliff:g id="APP_NAME2">%3$s</xliff:g> aplikazioak <xliff:g id="PERMISSIONS">%4$s</xliff:g> atzitu ahalko ditu <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> konektatuta dagoen bitartean."</string>
+ <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 66eafc3..6bb9620 100644
--- a/packages/CompanionDeviceManager/res/values-fa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"برای مدیریت کردن <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> به <xliff:g id="APP_NAME_0">%1$s</xliff:g> نیاز دارید. وقتی <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> متصل باشد، <xliff:g id="APP_NAME2">%3$s</xliff:g> به <xliff:g id="PERMISSIONS">%4$s</xliff:g> دسترسی پیدا خواهد کرد."</string>
+ <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 d4a20d9..5a9c1cd 100644
--- a/packages/CompanionDeviceManager/res/values-fi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> tarvitaan profiilin (<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>) hallinnointiin. <xliff:g id="APP_NAME2">%3$s</xliff:g> saa käyttöluvat (<xliff:g id="PERMISSIONS">%4$s</xliff:g>), kun <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> on yhdistetty."</string>
+ <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 e91ccf4..b31babd 100644
--- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"L\'application <xliff:g id="APP_NAME_0">%1$s</xliff:g> est requise pour gérer votre <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> aura accès à <xliff:g id="PERMISSIONS">%4$s</xliff:g> lorsque <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> est connecté."</string>
+ <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 756727a..08c93a2c 100644
--- a/packages/CompanionDeviceManager/res/values-fr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> est requis pour gérer votre <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> aura accès aux <xliff:g id="PERMISSIONS">%4$s</xliff:g> lorsque le/la <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> sera connecté(e)."</string>
+ <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 7849e42..c95b90e 100644
--- a/packages/CompanionDeviceManager/res/values-gl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Necesítase a aplicación <xliff:g id="APP_NAME_0">%1$s</xliff:g> para xestionar o teu perfil (<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>). <xliff:g id="APP_NAME2">%3$s</xliff:g> terá acceso a varios permisos (<xliff:g id="PERMISSIONS">%4$s</xliff:g>) mentres o perfil (<xliff:g id="PROFILE_NAME2">%5$s</xliff:g>) estea conectado."</string>
+ <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-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml
index 1f1edd1..e99a3cd 100644
--- a/packages/CompanionDeviceManager/res/values-gu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml
@@ -19,8 +19,11 @@
<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="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>
- <string name="profile_summary" msgid="3167701603666642104">"તમારા <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>ને મેનેજ કરવા માટે <xliff:g id="APP_NAME_0">%1$s</xliff:g> જરૂરી છે. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> જ્યારે કનેક્ટેડ હોય ત્યારે <xliff:g id="APP_NAME2">%3$s</xliff:g>ને <xliff:g id="PERMISSIONS">%4$s</xliff:g>નો ઍક્સેસ મળશે."</string>
+ <!-- no translation found for profile_summary (2009764182871566255) -->
+ <skip />
<string name="consent_yes" msgid="4055438216605487056">"હા"</string>
<string name="consent_no" msgid="1335543792857823917">"ના, આભાર"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml
index 6e280b5..ac95cc6 100644
--- a/packages/CompanionDeviceManager/res/values-hi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"आपके <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> को प्रबंधित करने के लिए, <xliff:g id="APP_NAME_0">%1$s</xliff:g> की ज़रूरत है. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> के कनेक्ट होने पर, <xliff:g id="APP_NAME2">%3$s</xliff:g> को <xliff:g id="PERMISSIONS">%4$s</xliff:g> का ऐक्सेस मिल जाएगा."</string>
+ <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 beacbfd..df8451f 100644
--- a/packages/CompanionDeviceManager/res/values-hr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Potrebna je <xliff:g id="APP_NAME_0">%1$s</xliff:g> za upravljanje vašim profilom <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. Aplikacija <xliff:g id="APP_NAME2">%3$s</xliff:g> dobit će pristup dopuštenju za <xliff:g id="PERMISSIONS">%4$s</xliff:g> dok je povezan profil <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>."</string>
+ <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 cd29006..ff1c6c5 100644
--- a/packages/CompanionDeviceManager/res/values-hu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Szükség van a(z) <xliff:g id="APP_NAME_0">%1$s</xliff:g> alkalmazásra a(z) <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> kezeléséhez. Amíg csatlakoztatva van a(z) <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>, a(z) <xliff:g id="APP_NAME2">%3$s</xliff:g> hozzáférést kap a következőkhöz: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+ <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 b0037aa..194223d 100644
--- a/packages/CompanionDeviceManager/res/values-hy/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Ձեր <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>ը կառավարելու համար անհրաժեշտ է <xliff:g id="APP_NAME_0">%1$s</xliff:g> հավելվածը։ Երբ <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>ը միացված լինի, <xliff:g id="APP_NAME2">%3$s</xliff:g> հավելվածին հասանելի կլինեն՝ <xliff:g id="PERMISSIONS">%4$s</xliff:g>։"</string>
+ <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 cc05490..58bf3cb 100644
--- a/packages/CompanionDeviceManager/res/values-in/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-in/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> diperlukan untuk mengelola <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> akan mendapatkan akses ke <xliff:g id="PERMISSIONS">%4$s</xliff:g> saat <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> terhubung."</string>
+ <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 d3ada74..cc5b989 100644
--- a/packages/CompanionDeviceManager/res/values-is/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> er krafist til að stjórna <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> fær aðgang að <xliff:g id="PERMISSIONS">%4$s</xliff:g> þegar <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> er tengt."</string>
+ <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 2632509..4cbefd8 100644
--- a/packages/CompanionDeviceManager/res/values-it/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-it/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"È richiesta l\'app <xliff:g id="APP_NAME_0">%1$s</xliff:g> per gestire il tuo <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> avrà accesso a <xliff:g id="PERMISSIONS">%4$s</xliff:g> quando <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> è connesso."</string>
+ <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-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml
index ae9d6f4..33950eb 100644
--- a/packages/CompanionDeviceManager/res/values-iw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -19,8 +19,11 @@
<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="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>
- <string name="profile_summary" msgid="3167701603666642104">"האפליקציה <xliff:g id="APP_NAME_0">%1$s</xliff:g> נדרשת לניהול של <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. האפליקציה <xliff:g id="APP_NAME2">%3$s</xliff:g> תקבל גישה אל <xliff:g id="PERMISSIONS">%4$s</xliff:g> כאשר <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> מחובר."</string>
+ <!-- no translation found for profile_summary (2009764182871566255) -->
+ <skip />
<string name="consent_yes" msgid="4055438216605487056">"כן"</string>
<string name="consent_no" msgid="1335543792857823917">"לא תודה"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml
index 3fb0140..b695d9d 100644
--- a/packages/CompanionDeviceManager/res/values-ja/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> を管理するために <xliff:g id="APP_NAME_0">%1$s</xliff:g> が必要です。<xliff:g id="PROFILE_NAME2">%5$s</xliff:g> の接続中に、<xliff:g id="APP_NAME2">%3$s</xliff:g> が <xliff:g id="PERMISSIONS">%4$s</xliff:g> にアクセスします。"</string>
+ <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 5b36106..300c94f 100644
--- a/packages/CompanionDeviceManager/res/values-ka/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"თქვენი <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>-ის სამართავად საჭიროა <xliff:g id="APP_NAME_0">%1$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> მიიღებს წვდომას <xliff:g id="PERMISSIONS">%4$s</xliff:g>-ზე, სანამ <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> დაკავშირებული იქნება."</string>
+ <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 6ff3f83..94d6c3e 100644
--- a/packages/CompanionDeviceManager/res/values-kk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> құрылғысын басқару үшін <xliff:g id="APP_NAME_0">%1$s</xliff:g> қолданбасы керек. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> құрылғысы жалғанған кезде<xliff:g id="APP_NAME2">%3$s</xliff:g> қолданбасы мына параметрлерді пайдалана алады: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+ <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 cdcebad..db13fe7 100644
--- a/packages/CompanionDeviceManager/res/values-km/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-km/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"ចាំបាច់ត្រូវមាន <xliff:g id="APP_NAME_0">%1$s</xliff:g> ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> របស់អ្នក។ <xliff:g id="APP_NAME2">%3$s</xliff:g> នឹងអាចចូលប្រើ <xliff:g id="PERMISSIONS">%4$s</xliff:g> នៅពេលភ្ជាប់ <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>។"</string>
+ <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-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml
index 47cf76c..f4ae18f 100644
--- a/packages/CompanionDeviceManager/res/values-kn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml
@@ -19,8 +19,11 @@
<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="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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> ಅನ್ನು ನಿರ್ವಹಿಸಲು, <xliff:g id="APP_NAME_0">%1$s</xliff:g> ಅಗತ್ಯವಿದೆ. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> ಕನೆಕ್ಟ್ ಆದಾಗ, <xliff:g id="PERMISSIONS">%4$s</xliff:g> ಗೆ <xliff:g id="APP_NAME2">%3$s</xliff:g> ಪ್ರವೇಶವನ್ನು ಪಡೆಯುತ್ತದೆ."</string>
+ <!-- no translation found for profile_summary (2009764182871566255) -->
+ <skip />
<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 f2904138..1363e57 100644
--- a/packages/CompanionDeviceManager/res/values-ko/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>을(를) 관리하려면 <xliff:g id="APP_NAME_0">%1$s</xliff:g> 앱이 필요합니다. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>이(가) 연결되어 있는 동안 <xliff:g id="APP_NAME2">%3$s</xliff:g> 앱이 <xliff:g id="PERMISSIONS">%4$s</xliff:g>에 액세스할 수 있습니다."</string>
+ <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 9cce298..c01e235 100644
--- a/packages/CompanionDeviceManager/res/values-ky/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> профилиңизди башкаруу үчүн <xliff:g id="APP_NAME_0">%1$s</xliff:g> керек. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> туташып турганда <xliff:g id="APP_NAME2">%3$s</xliff:g> колдонмосунун төмөнкүлөргө уруксаты болот: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+ <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 5fcbf7df..68218dd 100644
--- a/packages/CompanionDeviceManager/res/values-lo/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"ຕ້ອງໃຊ້ <xliff:g id="APP_NAME_0">%1$s</xliff:g> ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> ຂອງທ່ານ. <xliff:g id="APP_NAME2">%3$s</xliff:g> ຈະໄດ້ຮັບສິດເຂົ້າເຖິງ <xliff:g id="PERMISSIONS">%4$s</xliff:g> ໃນເວລາເຊື່ອມຕໍ່ <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>."</string>
+ <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 56930d3..5fd8280 100644
--- a/packages/CompanionDeviceManager/res/values-lt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Tam, kad būtų valdomas jūsų <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>, reikalinga programa „<xliff:g id="APP_NAME_0">%1$s</xliff:g>“. Kol prijungtas <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>, „<xliff:g id="APP_NAME2">%3$s</xliff:g>“ gaus prieigą prie šių elementų: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+ <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 a9d2151..bf036ec 100644
--- a/packages/CompanionDeviceManager/res/values-lv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Lai pārvaldītu profilu (<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>), nepieciešama lietotne <xliff:g id="APP_NAME_0">%1$s</xliff:g>. Kamēr profils (<xliff:g id="PROFILE_NAME2">%5$s</xliff:g>) būs pievienots, lietotnei <xliff:g id="APP_NAME2">%3$s</xliff:g> tiks piešķirta piekļuve šādām atļaujām: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+ <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 5cc18c5..427ca8f 100644
--- a/packages/CompanionDeviceManager/res/values-mk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> е потребна за да управува со <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> ќе добие пристап до <xliff:g id="PERMISSIONS">%4$s</xliff:g> додека <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> е поврзан."</string>
+ <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 b6734e8..a48c45f 100644
--- a/packages/CompanionDeviceManager/res/values-ml/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> എന്ന ആപ്പിന് നിങ്ങളുടെ <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> മാനേജ് ചെയ്യേണ്ടതുണ്ട്. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> കണക്റ്റ് ചെയ്തിരിക്കുമ്പോൾ <xliff:g id="APP_NAME2">%3$s</xliff:g> എന്ന ആപ്പിന് <xliff:g id="PERMISSIONS">%4$s</xliff:g> എന്നിവയിലേക്ക് ആക്സസ് ലഭിക്കും."</string>
+ <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 cd4fdbf..7ac20e6 100644
--- a/packages/CompanionDeviceManager/res/values-mn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Таны <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>-г удирдахын тулд <xliff:g id="APP_NAME_0">%1$s</xliff:g> шаардлагатай. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> холбогдсон үед <xliff:g id="APP_NAME2">%3$s</xliff:g> нь <xliff:g id="PERMISSIONS">%4$s</xliff:g>-д хандах эрхтэй болно."</string>
+ <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-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml
index 65bf262..68f9109 100644
--- a/packages/CompanionDeviceManager/res/values-mr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -19,8 +19,11 @@
<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="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>
- <string name="profile_summary" msgid="3167701603666642104">"तुमची <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> व्यवस्थापित करण्यासाठी <xliff:g id="APP_NAME_0">%1$s</xliff:g> आवश्यक आहे. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> कनेक्ट केलेली असताना <xliff:g id="APP_NAME2">%3$s</xliff:g> ला <xliff:g id="PERMISSIONS">%4$s</xliff:g> चा अॅक्सेस मिळेल."</string>
+ <!-- no translation found for profile_summary (2009764182871566255) -->
+ <skip />
<string name="consent_yes" msgid="4055438216605487056">"होय"</string>
<string name="consent_no" msgid="1335543792857823917">"नाही, नको"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml
index d17041f..1188922 100644
--- a/packages/CompanionDeviceManager/res/values-ms/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml
@@ -19,8 +19,11 @@
<string name="app_label" msgid="4470785958457506021">"Pengurus Peranti Rakan"</string>
<string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk diurus oleh <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"peranti"</string>
+ <!-- no translation found for profile_name_watch (576290739483672360) -->
+ <skip />
<string name="confirmation_title" msgid="4751119145078041732">"Tetapkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> untuk mengurus <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> anda"</string>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> diperlukan untuk mengurus <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> anda. <xliff:g id="APP_NAME2">%3$s</xliff:g> akan mendapat akses kepada <xliff:g id="PERMISSIONS">%4$s</xliff:g> semasa <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> disambungkan."</string>
+ <!-- no translation found for profile_summary (2009764182871566255) -->
+ <skip />
<string name="consent_yes" msgid="4055438216605487056">"Ya"</string>
<string name="consent_no" msgid="1335543792857823917">"Tidak perlu"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml
index 23f165a..9c2783c 100644
--- a/packages/CompanionDeviceManager/res/values-my/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-my/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"သင်၏ <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> ကို စီမံခန့်ခွဲရန် <xliff:g id="APP_NAME_0">%1$s</xliff:g> ကို လိုအပ်ပါသည်။ <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> ကို ချိတ်ဆက်ထားစဉ် <xliff:g id="APP_NAME2">%3$s</xliff:g> သည် <xliff:g id="PERMISSIONS">%4$s</xliff:g> ကို ဝင်သုံးခွင့်ရပါမည်။"</string>
+ <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 090f2a2..26fbb03 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> kreves for å administrere <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> får tilgang til <xliff:g id="PERMISSIONS">%4$s</xliff:g> når <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> er tilkoblet."</string>
+ <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 e885674..f289b37 100644
--- a/packages/CompanionDeviceManager/res/values-ne/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> व्यवस्थापन गर्न <xliff:g id="APP_NAME_0">%1$s</xliff:g> इन्स्टल गर्नु पर्ने हुन्छ। <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> कनेक्ट भएका बेला <xliff:g id="APP_NAME2">%3$s</xliff:g> ले <xliff:g id="PERMISSIONS">%4$s</xliff:g> प्रयोग गर्ने अनुमति प्राप्त गर्ने छ।"</string>
+ <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 5a7fb3a..0c9cdffd 100644
--- a/packages/CompanionDeviceManager/res/values-nl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Je hebt <xliff:g id="APP_NAME_0">%1$s</xliff:g> nodig om je <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> te beheren. <xliff:g id="APP_NAME2">%3$s</xliff:g> krijgt toegang tot <xliff:g id="PERMISSIONS">%4$s</xliff:g> terwijl je <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> is verbonden."</string>
+ <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-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml
index 03fae5c..d1aa50b 100644
--- a/packages/CompanionDeviceManager/res/values-or/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-or/strings.xml
@@ -19,8 +19,11 @@
<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="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>
- <string name="profile_summary" msgid="3167701603666642104">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">%1$s</xliff:g>ର ଆବଶ୍ୟକତା ଅଛି। <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> ସଂଯୁକ୍ତ ହୋଇଥିବା ସମୟରେ <xliff:g id="APP_NAME2">%3$s</xliff:g> <xliff:g id="PERMISSIONS">%4$s</xliff:g>କୁ ଆକ୍ସେସ୍ ପାଇବ।"</string>
+ <!-- no translation found for profile_summary (2009764182871566255) -->
+ <skip />
<string name="consent_yes" msgid="4055438216605487056">"ହଁ"</string>
<string name="consent_no" msgid="1335543792857823917">"ନା, ଧନ୍ୟବାଦ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml
index 33135b2..ff211f2 100644
--- a/packages/CompanionDeviceManager/res/values-pa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -19,8 +19,11 @@
<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="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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ਨੂੰ ਤੁਹਾਡਾ <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਲੋੜ ਹੈ। <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> ਕਨੈਕਟ ਕੀਤੇ ਹੋਣ \'ਤੇ <xliff:g id="APP_NAME2">%3$s</xliff:g> ਨੂੰ <xliff:g id="PERMISSIONS">%4$s</xliff:g> ਤੱਕ ਪਹੁੰਚ ਪ੍ਰਾਪਤ ਹੋ ਜਾਵੇਗੀ।"</string>
+ <!-- no translation found for profile_summary (2009764182871566255) -->
+ <skip />
<string name="consent_yes" msgid="4055438216605487056">"ਹਾਂ"</string>
<string name="consent_no" msgid="1335543792857823917">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml
index a828370..b07af57 100644
--- a/packages/CompanionDeviceManager/res/values-pl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Aplikacja <xliff:g id="APP_NAME_0">%1$s</xliff:g> jest wymagana do zarządzania profilem <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. Kiedy profil <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> będzie połączony, <xliff:g id="APP_NAME2">%3$s</xliff:g> będzie mieć te uprawnienia: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+ <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 4258e70..16906f6 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"O app <xliff:g id="APP_NAME_0">%1$s</xliff:g> é necessário para gerenciar seu <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> terá acesso a <xliff:g id="PERMISSIONS">%4$s</xliff:g> enquanto o <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> estiver conectado."</string>
+ <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-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
index 45b03d6..745d163 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
@@ -19,8 +19,9 @@
<string name="app_label" msgid="4470785958457506021">"Gestor de dispositivos associados"</string>
<string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerido pela app <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
<string name="confirmation_title" msgid="4751119145078041732">"Defina a app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> para gerir o seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="3167701603666642104">"A app <xliff:g id="APP_NAME_0">%1$s</xliff:g> é necessária para gerir o seu <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. A app <xliff:g id="APP_NAME2">%3$s</xliff:g> terá acesso a <xliff:g id="PERMISSIONS">%4$s</xliff:g> enquanto o <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> estiver associado."</string>
+ <string name="profile_summary" msgid="2009764182871566255">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> é necessária para gerir o 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">"Não, obrigado"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml
index 4258e70..16906f6 100644
--- a/packages/CompanionDeviceManager/res/values-pt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"O app <xliff:g id="APP_NAME_0">%1$s</xliff:g> é necessário para gerenciar seu <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> terá acesso a <xliff:g id="PERMISSIONS">%4$s</xliff:g> enquanto o <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> estiver conectado."</string>
+ <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 060e996..187cfbdf 100644
--- a/packages/CompanionDeviceManager/res/values-ro/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> este necesară pentru a vă gestiona profilul <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> va primi acces la <xliff:g id="PERMISSIONS">%4$s</xliff:g> în timp ce profilul <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> este conectat."</string>
+ <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 7982507..8dd9a39 100644
--- a/packages/CompanionDeviceManager/res/values-ru/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Приложение \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" необходимо для управления устройством (<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>). При подключении к устройству (<xliff:g id="PROFILE_NAME2">%5$s</xliff:g>) приложение \"<xliff:g id="APP_NAME2">%3$s</xliff:g>\" получит доступ к следующему: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+ <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 8bbc1a6..9e7c02e 100644
--- a/packages/CompanionDeviceManager/res/values-si/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-si/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ඔබගේ <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> කළමනාකරණය කිරීමට අවශ්යයි. <xliff:g id="APP_NAME2">%3$s</xliff:g> හට <xliff:g id="PERMISSIONS">%4$s</xliff:g> වෙත ප්රවේශය <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> සම්බන්ධිත අතරතුර ලැබෙනු ඇත."</string>
+ <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 1037a96..55a47c2 100644
--- a/packages/CompanionDeviceManager/res/values-sk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Na správu profilu <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> je potrebná aplikácia <xliff:g id="APP_NAME_0">%1$s</xliff:g>. Kým bude profil <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> pripojený, <xliff:g id="APP_NAME2">%3$s</xliff:g> získa prístup k povoleniam <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+ <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 f2d4c6f..159afd5 100644
--- a/packages/CompanionDeviceManager/res/values-sl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Za upravljanje naprave <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> potrebujete aplikacijo <xliff:g id="APP_NAME_0">%1$s</xliff:g>. Ko je naprava <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> povezana, bo aplikaciji <xliff:g id="APP_NAME2">%3$s</xliff:g> omogočen dostop do teh dovoljenj: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+ <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-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml
index c9336b3..4c308e8 100644
--- a/packages/CompanionDeviceManager/res/values-sq/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml
@@ -19,8 +19,11 @@
<string name="app_label" msgid="4470785958457506021">"Menaxheri i pajisjes shoqëruese"</string>
<string name="chooser_title" msgid="2262294130493605839">"Zgjidh një profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> që do të menaxhohet nga <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"pajisja"</string>
+ <!-- no translation found for profile_name_watch (576290739483672360) -->
+ <skip />
<string name="confirmation_title" msgid="4751119145078041732">"Cakto <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> që të menaxhojë profilin tënd <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="3167701603666642104">"Nevojitet <xliff:g id="APP_NAME_0">%1$s</xliff:g> për të menaxhuar profilin tënd <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> do të marrë qasje në <xliff:g id="PERMISSIONS">%4$s</xliff:g> ndërkohë që është lidhur profili <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>."</string>
+ <!-- no translation found for profile_summary (2009764182871566255) -->
+ <skip />
<string name="consent_yes" msgid="4055438216605487056">"Po"</string>
<string name="consent_no" msgid="1335543792857823917">"Jo, faleminderit"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml
index 5298194..fdbbe8e 100644
--- a/packages/CompanionDeviceManager/res/values-sr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Апликација <xliff:g id="APP_NAME_0">%1$s</xliff:g> је неопходна за управљање профилом <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> ће добити приступ дозволама за <xliff:g id="PERMISSIONS">%4$s</xliff:g> док је <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> повезан."</string>
+ <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 caacba0..bfd2516 100644
--- a/packages/CompanionDeviceManager/res/values-sv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> krävs för att hantera din <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> får åtkomst till <xliff:g id="PERMISSIONS">%4$s</xliff:g> medan <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> är ansluten."</string>
+ <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 ae8ade7..437ae7f 100644
--- a/packages/CompanionDeviceManager/res/values-sw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> linahitajika ili kudhibiti <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> wako. <xliff:g id="APP_NAME2">%3$s</xliff:g> itapata uwezo wa kufikia <xliff:g id="PERMISSIONS">%4$s</xliff:g> wakati <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> imeunganishwa."</string>
+ <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 373ed45..9b4a720 100644
--- a/packages/CompanionDeviceManager/res/values-ta/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> ஐ நிர்வகிக்க <xliff:g id="APP_NAME_0">%1$s</xliff:g> ஆப்ஸ் வேண்டும். <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> இணைக்கப்பட்டதும் <xliff:g id="PERMISSIONS">%4$s</xliff:g> ஆகியவற்றுக்கான அணுகலை <xliff:g id="APP_NAME2">%3$s</xliff:g> ஆப்ஸ் பெறும்."</string>
+ <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 f73e713..6e785de 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"మీ <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>ను మేనేజ్ చేయడానికి <xliff:g id="APP_NAME_0">%1$s</xliff:g> అవసరం ఉంది. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> కనెక్ట్ అయినప్పుడు <xliff:g id="APP_NAME2">%3$s</xliff:g>, <xliff:g id="PERMISSIONS">%4$s</xliff:g>కు యాక్సెస్ను పొందుతుంది."</string>
+ <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 8c1848a..b727d42 100644
--- a/packages/CompanionDeviceManager/res/values-th/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-th/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"ต้องใช้ <xliff:g id="APP_NAME_0">%1$s</xliff:g> ในการจัดการ<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> <xliff:g id="APP_NAME2">%3$s</xliff:g> จะได้รับสิทธิ์เข้าถึง<xliff:g id="PERMISSIONS">%4$s</xliff:g>ในขณะที่มีการเชื่อมต่อ<xliff:g id="PROFILE_NAME2">%5$s</xliff:g>"</string>
+ <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 8fcc3d2..a93282a 100644
--- a/packages/CompanionDeviceManager/res/values-tl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Kailangan ang <xliff:g id="APP_NAME_0">%1$s</xliff:g> para pamahalaan ang iyong <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. Magkakaroon ng access ang <xliff:g id="APP_NAME2">%3$s</xliff:g> sa <xliff:g id="PERMISSIONS">%4$s</xliff:g> habang nakakonekta ang <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>."</string>
+ <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 255eca5..3abe064 100644
--- a/packages/CompanionDeviceManager/res/values-tr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> yönetimi için gereklidir. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> bağlıyken <xliff:g id="APP_NAME2">%3$s</xliff:g>, şunlara erişebilecek: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+ <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 b5827f2..161d95e 100644
--- a/packages/CompanionDeviceManager/res/values-uk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Щоб керувати своїм пристроєм (<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>), вам потрібен додаток <xliff:g id="APP_NAME_0">%1$s</xliff:g>. Коли <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> буде підключено, додаток <xliff:g id="APP_NAME2">%3$s</xliff:g> отримає такі дозволи на доступ: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+ <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-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
index 2bbffdc..967b7f9 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -19,8 +19,11 @@
<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="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>
- <string name="profile_summary" msgid="3167701603666642104">"آپ کے <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> کا نظم کرنے کے لیے <xliff:g id="APP_NAME_0">%1$s</xliff:g> کی ضرورت ہے۔ <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> کے منسلک ہونے پر <xliff:g id="APP_NAME2">%3$s</xliff:g> <xliff:g id="PERMISSIONS">%4$s</xliff:g> تک رسائی حاصل کرے گا۔"</string>
+ <!-- no translation found for profile_summary (2009764182871566255) -->
+ <skip />
<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 96c49f2..4cce2e8 100644
--- a/packages/CompanionDeviceManager/res/values-uz/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ilovasi <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> qurilmasini boshqarish uchun kerak. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> qurilmasiga <xliff:g id="APP_NAME2">%3$s</xliff:g> ilovasi ulansa, u quyidagi ruxsatlarni oladi: <xliff:g id="PERMISSIONS">%4$s</xliff:g>"</string>
+ <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 d67db41..06a1ab6 100644
--- a/packages/CompanionDeviceManager/res/values-vi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"Cần có <xliff:g id="APP_NAME_0">%1$s</xliff:g> để quản lý <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> của bạn. <xliff:g id="APP_NAME2">%3$s</xliff:g> sẽ có quyền truy cập vào <xliff:g id="PERMISSIONS">%4$s</xliff:g> trong khi <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> được kết nối."</string>
+ <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 a1abd98..12bfcf3 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"若要管理<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>,您需要使用<xliff:g id="APP_NAME_0">%1$s</xliff:g>。在已连接<xliff:g id="PROFILE_NAME2">%5$s</xliff:g>的情况下,<xliff:g id="APP_NAME2">%3$s</xliff:g>将能够访问<xliff:g id="PERMISSIONS">%4$s</xliff:g>。"</string>
+ <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 57d2173..0c583b2 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"必須使用 <xliff:g id="APP_NAME_0">%1$s</xliff:g> 來管理您的<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>。連結<xliff:g id="PROFILE_NAME2">%5$s</xliff:g>後,<xliff:g id="APP_NAME2">%3$s</xliff:g> 將可以存取<xliff:g id="PERMISSIONS">%4$s</xliff:g>。"</string>
+ <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 c9a2fd8..519f0e8 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"如要管理你的<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>,必須使用「<xliff:g id="APP_NAME_0">%1$s</xliff:g>」。與<xliff:g id="PROFILE_NAME2">%5$s</xliff:g>連線時,「<xliff:g id="APP_NAME2">%3$s</xliff:g>」將有權存取你的<xliff:g id="PERMISSIONS">%4$s</xliff:g>。"</string>
+ <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 c811037..7721b54 100644
--- a/packages/CompanionDeviceManager/res/values-zu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml
@@ -19,8 +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>
+ <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>
- <string name="profile_summary" msgid="3167701603666642104">"I-<xliff:g id="APP_NAME_0">%1$s</xliff:g> iyadingeka ukuphatha i-<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> yakho. I-<xliff:g id="APP_NAME2">%3$s</xliff:g> izothola ukufinyelela ku-<xliff:g id="PERMISSIONS">%4$s</xliff:g> kuyilapho i-<xliff:g id="PROFILE_NAME2">%5$s</xliff:g> ixhunyiwe."</string>
+ <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/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml
index 77d252d..eb95466 100644
--- a/packages/InputDevices/res/values-eu/strings.xml
+++ b/packages/InputDevices/res/values-eu/strings.xml
@@ -48,6 +48,6 @@
<string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbaijandarra"</string>
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Poloniarra"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bielorrusiera"</string>
- <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongoliera"</string>
+ <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongoliarra"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiera"</string>
</resources>
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java
index fc93650..723caf2 100644
--- a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java
@@ -45,12 +45,15 @@
private AdaptiveConstantState mAdaptiveConstantState;
public AdaptiveIcon(Context context, Drawable foreground) {
+ this(context, foreground, R.dimen.dashboard_tile_foreground_image_inset);
+ }
+
+ public AdaptiveIcon(Context context, Drawable foreground, int insetResId) {
super(new Drawable[]{
new AdaptiveIconShapeDrawable(context.getResources()),
foreground
});
- final int insetPx = context.getResources()
- .getDimensionPixelSize(R.dimen.dashboard_tile_foreground_image_inset);
+ final int insetPx = context.getResources().getDimensionPixelSize(insetResId);
setLayerInset(1 /* index */, insetPx, insetPx, insetPx, insetPx);
mAdaptiveConstantState = new AdaptiveConstantState(context, foreground);
}
diff --git a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/apppreference/AppPreference.java b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppPreference.java
similarity index 71%
rename from packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/apppreference/AppPreference.java
rename to packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppPreference.java
index e88ac56..cfe7013 100644
--- a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/apppreference/AppPreference.java
+++ b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppPreference.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.widget.apppreference;
+package com.android.settingslib.widget;
import android.content.Context;
import android.util.AttributeSet;
@@ -24,13 +24,24 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
-import com.android.settingslib.widget.R;
-
+/**
+ * The Preference for the pages need to show apps icon.
+ */
public class AppPreference extends Preference {
private int mProgress;
private boolean mProgressVisible;
+ public AppPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ setLayoutResource(R.layout.preference_app);
+ }
+
+ public AppPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ setLayoutResource(R.layout.preference_app);
+ }
+
public AppPreference(Context context) {
super(context);
setLayoutResource(R.layout.preference_app);
@@ -41,6 +52,12 @@
setLayoutResource(R.layout.preference_app);
}
+ /**
+ * Sets the current progress.
+ * @param amount the current progress
+ *
+ * @see ProgressBar#setProgress(int)
+ */
public void setProgress(int amount) {
mProgress = amount;
mProgressVisible = true;
@@ -59,4 +76,4 @@
progress.setVisibility(View.GONE);
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java
new file mode 100644
index 0000000..781bfcd
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.preference.PreferenceViewHolder;
+import androidx.preference.SwitchPreference;
+
+/**
+ * The SwitchPreference for the pages need to show apps icon.
+ */
+public class AppSwitchPreference extends SwitchPreference {
+
+ public AppSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ setLayoutResource(R.layout.preference_app);
+ }
+
+ public AppSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ setLayoutResource(R.layout.preference_app);
+ }
+
+ public AppSwitchPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setLayoutResource(R.layout.preference_app);
+ }
+
+ public AppSwitchPreference(Context context) {
+ super(context);
+ setLayoutResource(R.layout.preference_app);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ final View switchView = holder.findViewById(android.R.id.switch_widget);
+ if (switchView != null) {
+ final View rootView = switchView.getRootView();
+ rootView.setFilterTouchesWhenObscured(true);
+ }
+ }
+}
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml
index b1553e9..7e3ce9d 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml
@@ -35,6 +35,7 @@
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
+ android:paddingRight="54dp"
android:layout_gravity="center_vertical"
android:maxLines="2"
android:ellipsize="end"
diff --git a/packages/SettingsLib/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-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index defbf54..6f4f72a 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF (diensverskaffer-wi-fi)"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobiele data is af"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Nie gestel om data te gebruik nie"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Geen foon nie."</string>
diff --git a/packages/SettingsLib/res/values-am/arrays.xml b/packages/SettingsLib/res/values-am/arrays.xml
index 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-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 1326c51..3f5df34 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"የተንቀሳቃሽ ስልክ ውሂብ ጠፍቷል"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"ውሂብን ለመጠቀም አልተቀናበረም"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"ምንም ስልክ የለም።"</string>
diff --git a/packages/SettingsLib/res/values-ar/arrays.xml b/packages/SettingsLib/res/values-ar/arrays.xml
index 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-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 702a419..0351d94 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -580,8 +580,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"شبكة الجيل الرابع أو أحدث"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"تم إيقاف بيانات الجوال"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"لم يتم الضبط على استخدام البيانات"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"ليست هناك إشارة بالهاتف."</string>
diff --git a/packages/SettingsLib/res/values-as/arrays.xml b/packages/SettingsLib/res/values-as/arrays.xml
index 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-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index c5dbda9..0f7db8f 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"এলটিই"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"এলটিই+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"ম’বাইল ডেটা অফ অৱস্থাত আছে"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"ডেটা ব্যৱহাৰ কৰিবলৈ ছেট কৰা নাই"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"ফ\'নত ছিগনেল নাই৷"</string>
diff --git a/packages/SettingsLib/res/values-az/arrays.xml b/packages/SettingsLib/res/values-az/arrays.xml
index 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-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index b18b1c4..0855d17e 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobil data deaktivdir"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Data istifadə etmək üçün ayarlanmayıb"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Telefon yoxdur."</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
index 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-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index d92f171..3386fe86 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -577,8 +577,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"WiFi mobilnog operatera"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobilni podaci su isključeni"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Nije podešeno za korišćenje podataka"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Nema telefona."</string>
diff --git a/packages/SettingsLib/res/values-be/arrays.xml b/packages/SettingsLib/res/values-be/arrays.xml
index 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-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 57d9a55..6ac2172 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -578,8 +578,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Мабільная перадача даных выключана"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Не зададзена для выкарыстання даных"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Няма тэлефона."</string>
diff --git a/packages/SettingsLib/res/values-bg/arrays.xml b/packages/SettingsLib/res/values-bg/arrays.xml
index 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-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 4d4b392..19ed5bd 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Мобилните данни са изключени"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Не е зададено да използва данни"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Няма телефон."</string>
diff --git a/packages/SettingsLib/res/values-bn/arrays.xml b/packages/SettingsLib/res/values-bn/arrays.xml
index 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-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 88fb83f..b0e9342 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"মোবাইল ডেটা বন্ধ করা হয়েছে"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"ডেটা ব্যবহার করার জন্য সেট করা নেই"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"কোনো ফোনের সংকেত নেই৷"</string>
diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml
index 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-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 6925d04..9377624 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -577,8 +577,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Prijenos podataka na mobilnoj mreži je isključen"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Nije postavljeno za korištenje podataka"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Nema telefonskog signala."</string>
diff --git a/packages/SettingsLib/res/values-ca/arrays.xml b/packages/SettingsLib/res/values-ca/arrays.xml
index 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-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index dd3a811..a2a7770 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"S\'han desactivat les dades mòbils"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"No s\'ha definit per utilitzar dades"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"No hi ha senyal de telèfon."</string>
diff --git a/packages/SettingsLib/res/values-cs/arrays.xml b/packages/SettingsLib/res/values-cs/arrays.xml
index 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-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 6b8914d..4db4d5c 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -578,8 +578,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"Wi-Fi operátora"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobilní data jsou vypnuta"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Nenastaveno k využití dat"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Žádná telefonní síť."</string>
diff --git a/packages/SettingsLib/res/values-da/arrays.xml b/packages/SettingsLib/res/values-da/arrays.xml
index 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-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index c8b4e2b..b76afa5c 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobildata er deaktiveret"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Ikke indstillet til at anvende data"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Ingen telefon."</string>
diff --git a/packages/SettingsLib/res/values-de/arrays.xml b/packages/SettingsLib/res/values-de/arrays.xml
index 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-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index eb87a58..c608a62 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Τα δεδομένα κινητής τηλεφωνίας απενεργοποιήθηκαν"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Δεν ρυθμίστηκε ώστε να χρησιμοποιεί δεδομένα"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Δεν υπάρχει τηλέφωνο."</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/arrays.xml b/packages/SettingsLib/res/values-en-rAU/arrays.xml
index 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-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index ad16c89..e67c3d1 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobile data off"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Not set to use data"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"No phone."</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/arrays.xml b/packages/SettingsLib/res/values-en-rCA/arrays.xml
index 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-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 4515908..b0830fc 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobile data off"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Not set to use data"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"No phone."</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/arrays.xml b/packages/SettingsLib/res/values-en-rGB/arrays.xml
index 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-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index ad16c89..e67c3d1 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobile data off"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Not set to use data"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"No phone."</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/arrays.xml b/packages/SettingsLib/res/values-en-rIN/arrays.xml
index 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-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index ad16c89..e67c3d1 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobile data off"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Not set to use data"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"No phone."</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/arrays.xml b/packages/SettingsLib/res/values-en-rXC/arrays.xml
index 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-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index f8903bb..4c0af75 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobile data off"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Not set to use data"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"No phone."</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/arrays.xml b/packages/SettingsLib/res/values-es-rUS/arrays.xml
index 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-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 26cffd2..9ae7781 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Datos móviles desactivados"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"No se configuró para usar datos"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Sin teléfono"</string>
diff --git a/packages/SettingsLib/res/values-es/arrays.xml b/packages/SettingsLib/res/values-es/arrays.xml
index 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-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 5a58a20..203726a 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Datos desactiv."</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"No está establecido para usar los datos"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Sin teléfono"</string>
diff --git a/packages/SettingsLib/res/values-et/arrays.xml b/packages/SettingsLib/res/values-et/arrays.xml
index 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-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 2d5d0b5..379ed6c 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobiilne andmeside on väljas"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Ei ole andmeside kasutamiseks seadistatud"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Telefonisignaal puudub"</string>
diff --git a/packages/SettingsLib/res/values-eu/arrays.xml b/packages/SettingsLib/res/values-eu/arrays.xml
index 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-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index ae99c01..cb598e5 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"داده تلفن همراه خاموش است"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"برای استفاده از داده تنظیم نشده است"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"بدون تلفن."</string>
diff --git a/packages/SettingsLib/res/values-fi/arrays.xml b/packages/SettingsLib/res/values-fi/arrays.xml
index 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-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 6dd3a21d..31dfe18 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"Operaattorin Wi-Fi"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobiilidata poistettu käytöstä"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Ei käytä dataa"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Ei puhelinverkkoyhteyttä."</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/arrays.xml b/packages/SettingsLib/res/values-fr-rCA/arrays.xml
index 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-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index cf8ec40..46bd71b 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"મોબાઇલ ડેટા બંધ છે"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"ડેટાનો ઉપયોગ કરવાનું સેટ કર્યું નથી"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"કોઈ ફોન નથી."</string>
diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml
index 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-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index ddcfc58..9cae311 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"एलटीई"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"मोबाइल डेटा बंद है"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"डेटा इस्तेमाल करने के लिए सेट नहीं किया गया है"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"कोई फ़ोन नहीं."</string>
diff --git a/packages/SettingsLib/res/values-hr/arrays.xml b/packages/SettingsLib/res/values-hr/arrays.xml
index 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-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index f7d2bec..83bb2d1 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -577,8 +577,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G i više"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobilni su podaci isključeni"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Nije postavljeno za upotrebu podataka"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Nema telefona."</string>
diff --git a/packages/SettingsLib/res/values-hu/arrays.xml b/packages/SettingsLib/res/values-hu/arrays.xml
index 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-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 883e2a4..9f184c7 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"Szolgáltatói Wi-Fi"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobiladatok kikapcsolva"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Nincs beállítva az adathasználat"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Nincs telefon."</string>
diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml
index 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-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index de9e4dd..cd6cbf3 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Բջջային ինտերնետն անջատված է"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Բջջային ինտերնետն ըստ կանխադրման չի օգտագործվում"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Հեռախոս չկա:"</string>
diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml
index 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-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index e4f56e6..3b80918 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Kuota nonaktif"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Tidak disetel untuk menggunakan data"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Tidak dapat melakukan panggilan."</string>
diff --git a/packages/SettingsLib/res/values-is/arrays.xml b/packages/SettingsLib/res/values-is/arrays.xml
index 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-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index d5d8289..ce9e665 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"Wi-Fi símafyrirtækis (CWF)"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Slökkt á farsímagögnum"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Ekki stillt á að nota gögn"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Ekkert símasamband."</string>
diff --git a/packages/SettingsLib/res/values-it/arrays.xml b/packages/SettingsLib/res/values-it/arrays.xml
index 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-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 55c03b6..93b24a0 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"Wi-Fi operatore"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Dati mobili disattivati"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Non impostato per l\'utilizzo dei dati"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Nessun telefono."</string>
diff --git a/packages/SettingsLib/res/values-iw/arrays.xml b/packages/SettingsLib/res/values-iw/arrays.xml
index 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-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 6bbfa16..ab67809 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -578,8 +578,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"+4G"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"+LTE"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"Wi-Fi של הספק"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"חבילת הגלישה כבויה"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"לא מוגדרת לשימוש בנתונים"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"אין טלפון."</string>
diff --git a/packages/SettingsLib/res/values-ja/arrays.xml b/packages/SettingsLib/res/values-ja/arrays.xml
index 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-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index d97926b..0813305 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"მობილური ინტერნეტი გამორთულია"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"არ არის დაყენებული მონაცემების გამოყენებისთვის"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"ტელეფონი არ არის."</string>
diff --git a/packages/SettingsLib/res/values-kk/arrays.xml b/packages/SettingsLib/res/values-kk/arrays.xml
index 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-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 6cceef6..16ee9c0 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -359,7 +359,7 @@
<string name="enable_gpu_debug_layers" msgid="4986675516188740397">"GPU жөндеу қабаттарын қосу"</string>
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"GPU жөндеу қабаттарының жүктелуіне рұқсат ету"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Жеткізушілерді журналға тіркеу"</string>
- <string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Қате туралы есепте қызмет көрсетушінің құрылғыға қатысты қосымша ақпаратын қамту. Мұнда жеке ақпарат көрсетілуі, батарея шығыны артуы және/немесе қосымша жад пайдаланылуы мүмкін."</string>
+ <string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Қате туралы есепте жеткізушінің құрылғыға қатысты қосымша ақпараты қамтылады. Мұнда жеке ақпарат көрсетілуі, батарея шығыны артуы және/немесе қосымша жад пайдаланылуы мүмкін."</string>
<string name="window_animation_scale_title" msgid="5236381298376812508">"Терезе анимациясының өлшемі"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Ауысу анимациясының өлшемі"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Аниматор ұзақтығы"</string>
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Мобильдік деректер өшірулі"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Деректерді пайдалануға реттелмеген."</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Телефон жоқ."</string>
diff --git a/packages/SettingsLib/res/values-km/arrays.xml b/packages/SettingsLib/res/values-km/arrays.xml
index 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-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index f069d15..5b47381 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"ទិន្នន័យទូរសព្ទចល័តបានបិទ"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"មិនបានកំណត់ឱ្យប្រើទិន្នន័យទេ"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"គ្មានទូរស័ព្ទ។"</string>
diff --git a/packages/SettingsLib/res/values-kn/arrays.xml b/packages/SettingsLib/res/values-kn/arrays.xml
index 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-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 3841e3b..fd63dcb 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"ಮೊಬೈಲ್ ಡೇಟಾ ಆಫ್"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"ಡೇಟಾ ಬಳಸಲು ಹೊಂದಿಸಲಾಗಿಲ್ಲ"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"ಯಾವುದೇ ಫೋನ್ ಇಲ್ಲ."</string>
diff --git a/packages/SettingsLib/res/values-ko/arrays.xml b/packages/SettingsLib/res/values-ko/arrays.xml
index 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-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 75b731b..a9c3f31 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G 이상"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"모바일 데이터 꺼짐"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"데이터를 사용하도록 설정되지 않음"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"휴대전화의 신호가 없습니다."</string>
diff --git a/packages/SettingsLib/res/values-ky/arrays.xml b/packages/SettingsLib/res/values-ky/arrays.xml
index 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-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 1159376..bc3656f 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Мобилдик Интернет өчүрүлгөн"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Дайындарды колдонуу үчүн жөндөлгөн эмес"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Телефон сигналы жок."</string>
diff --git a/packages/SettingsLib/res/values-lo/arrays.xml b/packages/SettingsLib/res/values-lo/arrays.xml
index 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-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 3e3f317..8408c93 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"ປິດອິນເຕີເນັດມືຖືແລ້ວ"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"ບໍ່ໄດ້ຕັ້ງໃຫ້ໃຊ້ອິນເຕີເນັດ"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"ບໍ່ມີໂທລະສັບ."</string>
diff --git a/packages/SettingsLib/res/values-lt/arrays.xml b/packages/SettingsLib/res/values-lt/arrays.xml
index 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-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 279980b..1dcfcf7 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -578,8 +578,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobiliojo ryšio duomenys išjungti"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Nenustatyta naudoti duomenis"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Nėra telefono."</string>
diff --git a/packages/SettingsLib/res/values-lv/arrays.xml b/packages/SettingsLib/res/values-lv/arrays.xml
index 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-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index f36adc6..3d9b78a 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -577,8 +577,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobilie dati izslēgti"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Nav iestatīts datu lietošanai"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Nav tālruņa."</string>
diff --git a/packages/SettingsLib/res/values-mk/arrays.xml b/packages/SettingsLib/res/values-mk/arrays.xml
index 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-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 31e5b7c..0674f11 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"Wi-Fi на операторот"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Мобилниот интернет е исклучен"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Не е поставен да користи интернет"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Нема сигнал."</string>
diff --git a/packages/SettingsLib/res/values-ml/arrays.xml b/packages/SettingsLib/res/values-ml/arrays.xml
index 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-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 6ce0a7d..f13cb0b 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Мобайл дата унтраалттай байна"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Дата ашиглахаар тохируулаагүй"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Утас байхгүй."</string>
diff --git a/packages/SettingsLib/res/values-mr/arrays.xml b/packages/SettingsLib/res/values-mr/arrays.xml
index 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-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index c1a246f..f5be76e 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -168,7 +168,7 @@
<string name="tts_play_example_summary" msgid="634044730710636383">"उच्चार संश्लेषणाचे एक छोटेसे प्रात्यक्षिक प्ले करा"</string>
<string name="tts_install_data_title" msgid="1829942496472751703">"व्हॉइस डेटा इंस्टॉल करा"</string>
<string name="tts_install_data_summary" msgid="3608874324992243851">"उच्चार संश्लेषणासाठी आवश्यक आवाज डेटा इंस्टॉल करा"</string>
- <string name="tts_engine_security_warning" msgid="3372432853837988146">"हे उच्चार संश्लेषण इंजिन पासवर्ड आणि क्रेडिट कार्ड नंबर यासारख्या वैयक्तिक मजकुरासह, बोलला जाणारा सर्व मजकूर संकलित करण्यात सक्षम होऊ शकते. हे <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> इंजिनवरून येते. या उच्चार संश्लेषण इंजिनचा वापर सक्षम करायचा?"</string>
+ <string name="tts_engine_security_warning" msgid="3372432853837988146">"हे उच्चार संश्लेषण इंजीन पासवर्ड आणि क्रेडिट कार्ड नंबर यासारख्या वैयक्तिक मजकुरासह, बोलला जाणारा सर्व मजकूर संकलित करण्यात सक्षम होऊ शकते. हे <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> इंजीनवरून येते. या उच्चार संश्लेषण इंजीनचा वापर सक्षम करायचा?"</string>
<string name="tts_engine_network_required" msgid="8722087649733906851">"या भाषेस टेक्स्ट टू स्पीचसाठी एका नेटवर्क कनेक्शनची आवश्यकता आहे."</string>
<string name="tts_default_sample_string" msgid="6388016028292967973">"हे उच्चार संश्लेषणाचे एक उदाहरण आहे"</string>
<string name="tts_status_title" msgid="8190784181389278640">"डीफॉल्ट भाषा स्थिती"</string>
@@ -177,8 +177,8 @@
<string name="tts_status_not_supported" msgid="2702997696245523743">"<xliff:g id="LOCALE">%1$s</xliff:g> समर्थित नाही"</string>
<string name="tts_status_checking" msgid="8026559918948285013">"तपासत आहे..."</string>
<string name="tts_engine_settings_title" msgid="7849477533103566291">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> साठी सेटिंग्ज"</string>
- <string name="tts_engine_settings_button" msgid="477155276199968948">"इंजिन सेटिंग्ज लाँच करा"</string>
- <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"प्राधान्य इंजिन"</string>
+ <string name="tts_engine_settings_button" msgid="477155276199968948">"इंजीन सेटिंग्ज लाँच करा"</string>
+ <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"प्राधान्य इंजीन"</string>
<string name="tts_general_section_title" msgid="8919671529502364567">"सामान्य"</string>
<string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"उच्चार पिच रीसेट करा"</string>
<string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"डीफॉल्टवर मजकूर ज्या पिचवर बोलला जातो तो रीसेट करा."</string>
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-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 61374e8..1dcb3cf 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"မိုဘိုင်းဒေတာ ပိတ်ထားသည်"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"ဒေတာအသုံးပြုရန် သတ်မှတ်မထားပါ"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"ဖုန်းလိုင်းမရှိပါ။"</string>
diff --git a/packages/SettingsLib/res/values-nb/arrays.xml b/packages/SettingsLib/res/values-nb/arrays.xml
index 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-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index fe6204b..13f69c1 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobildata er slått av"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Ikke konfigurert til å bruke data"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Ingen telefon."</string>
diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml
index 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-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index b7ea8a0..718ce5d 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobiele data uit"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Gebruik van gegevens is niet ingesteld"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Geen telefoonsignaal."</string>
diff --git a/packages/SettingsLib/res/values-or/arrays.xml b/packages/SettingsLib/res/values-or/arrays.xml
index 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-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index fc26f59..f24b52b 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Dados móveis desativados"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Sem configuração para uso de dados"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Sem telefone."</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/arrays.xml b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
index 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-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 7ff1ba4..42ad0fe 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"WFO"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Dados móveis desativados"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Não definido para utilizar dados"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Sem telefone."</string>
diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml
index 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-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index fc26f59..f24b52b 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Dados móveis desativados"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Sem configuração para uso de dados"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Sem telefone."</string>
diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml
index 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-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index be0193c..0743fe9 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -577,8 +577,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"Rețeaua Wi‑Fi a operatorului"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Date mobile dezactivate"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Nu este setat pentru a folosi datele"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Nu există semnal pentru telefon."</string>
diff --git a/packages/SettingsLib/res/values-ru/arrays.xml b/packages/SettingsLib/res/values-ru/arrays.xml
index 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-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index fad3591..d19438a 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -578,8 +578,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Мобильный Интернет отключен"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Мобильный Интернет по умолчанию не используется."</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Сигнал телефонной сети отсутствует."</string>
diff --git a/packages/SettingsLib/res/values-si/arrays.xml b/packages/SettingsLib/res/values-si/arrays.xml
index 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-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 69a918a..15fe8c8 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"ජංගම දත්ත ක්රියාවිරහිතයි"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"දත්ත භාවිත කිරීමට සකසා නැත"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"දුරකථනයක් නැත."</string>
diff --git a/packages/SettingsLib/res/values-sk/arrays.xml b/packages/SettingsLib/res/values-sk/arrays.xml
index 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-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index f98237c2..ee53b7c 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -578,8 +578,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"Wi‑Fi operátora"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobilné dáta sú vypnuté"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Nie je nastavené na používanie dát"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Žiadna telefónna sieť."</string>
diff --git a/packages/SettingsLib/res/values-sl/arrays.xml b/packages/SettingsLib/res/values-sl/arrays.xml
index 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-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 710fbdb..66d33a7 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -578,8 +578,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"Omrežje Wi‑Fi operaterja"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Prenos podatkov v mobilnem omrežju je izklopljen"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Ni nastavljeno za uporabo prenosa podatkov"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Ni telefona."</string>
diff --git a/packages/SettingsLib/res/values-sq/arrays.xml b/packages/SettingsLib/res/values-sq/arrays.xml
index 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-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index d134f8a..ce74b84 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -577,8 +577,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"WiFi мобилног оператера"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Мобилни подаци су искључени"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Није подешено за коришћење података"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Нема телефона."</string>
diff --git a/packages/SettingsLib/res/values-sv/arrays.xml b/packages/SettingsLib/res/values-sv/arrays.xml
index 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-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index e90a915..eacb7a8 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"Operatörens Wi-Fi"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobildata har inaktiverats"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Inte inställd på mobildata"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Ingen telefon."</string>
diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml
index 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-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 653beef..e7045a7 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Umezima data ya mtandao wa simu"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Haijawekewa mipangilio ya kutumia data"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Hakuna simu"</string>
diff --git a/packages/SettingsLib/res/values-ta/arrays.xml b/packages/SettingsLib/res/values-ta/arrays.xml
index 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-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index b75818a..06c7ccb 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"மொபைல் டேட்டா ஆஃப் செய்யப்பட்டது"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"தரவை உபயோகிக்க அமைக்கப்படவில்லை"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"சிக்னல் இல்லை."</string>
diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml
index 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-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index a010c10..6316452 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"เน็ตมือถือปิดอยู่"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"ไม่ได้ตั้งค่าให้ใช้อินเทอร์เน็ตมือถือ"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"ไม่มีสัญญาณโทรศัพท์"</string>
diff --git a/packages/SettingsLib/res/values-tl/arrays.xml b/packages/SettingsLib/res/values-tl/arrays.xml
index 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-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 4485384..0aabbe5 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Naka-off ang mobile data"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Hindi nakatakdang gumamit ng data"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Walang telepono."</string>
diff --git a/packages/SettingsLib/res/values-tr/arrays.xml b/packages/SettingsLib/res/values-tr/arrays.xml
index 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-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 081a115..44c8f13 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobil veri kapalı"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Veri kullanmak üzere ayarlanmadı"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Telefon sinyali yok."</string>
diff --git a/packages/SettingsLib/res/values-uk/arrays.xml b/packages/SettingsLib/res/values-uk/arrays.xml
index 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-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index b4e533d..7851111 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -578,8 +578,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"Мережа Wi-Fi оператора"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Мобільне передавання даних вимкнено"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Не вибрано для використання даних"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Немає сигналу телефону."</string>
diff --git a/packages/SettingsLib/res/values-ur/arrays.xml b/packages/SettingsLib/res/values-ur/arrays.xml
index 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-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 4787aeb..c2a96c6 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Mobil internet yoqilmagan"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Maʼlumotlardan foydalanish uchun sozlanmagan"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Signal yo‘q."</string>
diff --git a/packages/SettingsLib/res/values-vi/arrays.xml b/packages/SettingsLib/res/values-vi/arrays.xml
index 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-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index df975b3..2596424 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Đã tắt dữ liệu di động"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Chưa được đặt để sử dụng dữ liệu"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Không có điện thoại nào."</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/arrays.xml b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
index 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-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index e1b6047..60afd6d 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"移动数据网络已关闭"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"未设置为使用移动数据"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"没有手机信号。"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/arrays.xml b/packages/SettingsLib/res/values-zh-rHK/arrays.xml
index 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-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index e095afd..925f738 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"流動網絡供應商 Wi-Fi"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"流動數據已關閉"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"未設定至可使用資料"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"沒有電話訊號。"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/arrays.xml b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
index 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-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index a1dc7f1..e40d351 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"行動數據已關閉"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"並未設為使用行動數據"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"沒有電話訊號。"</string>
diff --git a/packages/SettingsLib/res/values-zu/arrays.xml b/packages/SettingsLib/res/values-zu/arrays.xml
index 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/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index a04f47c..c304c14 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -576,8 +576,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"I-LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"I-LTE+"</string>
- <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
- <skip />
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"I-CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Idatha yeselula ivaliwe"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Akusethiwe ukuze kusetshenziswe idatha"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Ayikho ifoni."</string>
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index c63cf06..2b5e9cd 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -291,7 +291,7 @@
<item>256K</item>
<item>1M</item>
<item>4M</item>
- <item>16M</item>
+ <item>8M</item>
</string-array>
<!-- Titles for logd limit size lowram selection preference. [CHAR LIMIT=14] -->
@@ -309,7 +309,7 @@
<item>262144</item>
<item>1048576</item>
<item>4194304</item>
- <item>16777216</item>
+ <item>8388608</item>
</string-array>
<!-- Summaries for logd limit size selection preference. [CHAR LIMIT=50]-->
@@ -319,7 +319,7 @@
<item>256K per log buffer</item>
<item>1M per log buffer</item>
<item>4M per log buffer</item>
- <item>16M per log buffer</item>
+ <item>8M per log buffer</item>
</string-array>
<!-- Values for logpersist state selection preference. -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 4614694..15bacbf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -171,6 +171,10 @@
} else {
ssid = getValidSsid(mWifiInfo);
}
+ if (mProviderModel) {
+ isCarrierMerged = mWifiInfo.isCarrierMerged();
+ subId = mWifiInfo.getSubscriptionId();
+ }
updateRssi(mWifiInfo.getRssi());
maybeRequestNetworkScore();
}
@@ -211,6 +215,8 @@
private void updateWifiState() {
state = mWifiManager.getWifiState();
enabled = state == WifiManager.WIFI_STATE_ENABLED;
+ isCarrierMerged = false;
+ subId = 0;
}
private void updateRssi(int newRssi) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppPreferenceTest.java
similarity index 92%
rename from packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java
rename to packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppPreferenceTest.java
index 2848888..9e265a4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppPreferenceTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.widget.apppreference;
+package com.android.settingslib.widget;
import static com.google.common.truth.Truth.assertThat;
@@ -23,8 +23,6 @@
import androidx.preference.PreferenceViewHolder;
-import com.android.settingslib.widget.R;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 53e67e1..fbc71f1 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -264,7 +264,6 @@
VALIDATORS.put(Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.EMERGENCY_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.EMERGENCY_GESTURE_SOUND_ENABLED, BOOLEAN_VALIDATOR);
- VALIDATORS.put(Secure.EMERGENCY_GESTURE_CALL_NUMBER, NONE_NEGATIVE_LONG_VALIDATOR);
VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR);
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/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index e22f264..07a5a44 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -742,7 +742,6 @@
Settings.Secure.SKIP_GESTURE,
Settings.Secure.SILENCE_GESTURE,
Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
- Settings.Secure.EMERGENCY_GESTURE_CALL_NUMBER,
Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
Settings.Secure.FACE_UNLOCK_RE_ENROLL,
Settings.Secure.TAP_GESTURE,
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 14151191..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"/>
@@ -157,6 +158,7 @@
<uses-permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS" />
<uses-permission android:name="android.permission.MANAGE_APP_PREDICTIONS" />
<uses-permission android:name="android.permission.MANAGE_SEARCH_UI" />
+ <uses-permission android:name="android.permission.MANAGE_SMARTSPACE" />
<uses-permission android:name="android.permission.NETWORK_SETTINGS" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.SET_TIME" />
@@ -369,8 +371,9 @@
<!-- Permissions required for CTS tests to close system dialogs -->
<uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" />
- <!-- Permission required for CTS test - HideOverlayWindowsTest -->
+ <!-- Permissions required for CTS test - HideOverlayWindowsTest -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+ <uses-permission android:name="android.permission.SYSTEM_APPLICATION_OVERLAY"/>
<!-- Permission required for CTS test - CtsHdmiCecHostTestCases -->
<uses-permission android:name="android.permission.HDMI_CEC" />
@@ -382,6 +385,9 @@
<!-- Permission required for CTS tests to enable/disable rate limiting toasts. -->
<uses-permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING" />
+ <!-- Permission required for CTS to test sensor privacy behavior -->
+ <uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SimAppDialog/res/values-mn/strings.xml b/packages/SimAppDialog/res/values-mn/strings.xml
index f21b80b..51298bb 100644
--- a/packages/SimAppDialog/res/values-mn/strings.xml
+++ b/packages/SimAppDialog/res/values-mn/strings.xml
@@ -19,8 +19,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="8898068901680117589">"Sim аппын харилцах цонх"</string>
<string name="install_carrier_app_title" msgid="334729104862562585">"Мобайл үйлчилгээг идэвхжүүлэх"</string>
- <string name="install_carrier_app_description" msgid="4014303558674923797">"Та шинэ СИМ-ээ зөв ажиллуулахын тулд <xliff:g id="ID_1">%1$s</xliff:g> аппыг суулгах хэрэгтэй болно"</string>
- <string name="install_carrier_app_description_default" msgid="7356830245205847840">"Та шинэ СИМ-ээ зөв ажиллуулахын тулд оператор компанийхаа аппыг суулгах хэрэгтэй болно"</string>
+ <string name="install_carrier_app_description" msgid="4014303558674923797">"Та шинэ SIM-ээ зөв ажиллуулахын тулд <xliff:g id="ID_1">%1$s</xliff:g> аппыг суулгах хэрэгтэй болно"</string>
+ <string name="install_carrier_app_description_default" msgid="7356830245205847840">"Та шинэ SIM-ээ зөв ажиллуулахын тулд оператор компанийхаа аппыг суулгах хэрэгтэй болно"</string>
<string name="install_carrier_app_defer_action" msgid="2558576736886876209">"Одоо биш"</string>
<string name="install_carrier_app_download_action" msgid="7859229305958538064">"Апп татах"</string>
</resources>
diff --git a/packages/SystemUI/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-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 55bdebd..80c8a28 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -51,7 +51,6 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:gravity="center_horizontal"
- android:letterSpacing="0.03"
android:textColor="?attr/wallpaperTextColor"
android:singleLine="true"
style="@style/widget_title_bold"
@@ -72,12 +71,12 @@
android:id="@+id/animatable_clock_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="right"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
android:textSize="100dp"
- android:letterSpacing="0.02"
- android:lineSpacingMultiplier=".8"
android:includeFontPadding="false"
android:fontFamily="@font/clock"
+ android:lineSpacingMultiplier=".65"
android:typeface="monospace"
android:elegantTextHeight="false"
dozeWeight="200"
@@ -97,9 +96,8 @@
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:textSize="@dimen/large_clock_text_size"
- android:letterSpacing="0.02"
- android:lineSpacingMultiplier=".8"
android:includeFontPadding="false"
+ android:lineSpacingMultiplier=".65"
android:fontFamily="@font/clock"
android:typeface="monospace"
android:elegantTextHeight="false"
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/ic_qs_no_internet_airplane.xml b/packages/SystemUI/res/drawable/ic_qs_no_internet_airplane.xml
new file mode 100644
index 0000000..3d6ca7a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_no_internet_airplane.xml
@@ -0,0 +1,28 @@
+<!--
+ 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.
+-->
+<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="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10c0.34,0 0.68,-0.02 1.01,-0.05V20h-1v-0.04c-0.83,-1.2 -1.48,-2.53 -1.91,-3.96H13v-2H9.66c-0.09,-0.66 -0.16,-1.32 -0.16,-2s0.07,-1.35 0.16,-2H21.8C20.87,5.44 16.83,2 11.99,2zM18.92,8h-2.95c-0.32,-1.25 -0.78,-2.45 -1.38,-3.56C16.43,5.07 17.96,6.35 18.92,8zM12,4.04c0.83,1.2 1.48,2.53 1.91,3.96h-3.82C10.52,6.57 11.17,5.24 12,4.04zM4.26,14C4.1,13.36 4,12.69 4,12s0.1,-1.36 0.26,-2h3.38c-0.08,0.66 -0.14,1.32 -0.14,2s0.06,1.34 0.14,2H4.26zM5.08,16h2.95c0.32,1.25 0.78,2.45 1.38,3.56C7.57,18.93 6.04,17.66 5.08,16zM8.03,8H5.08c0.96,-1.66 2.49,-2.93 4.33,-3.56C8.81,5.55 8.35,6.75 8.03,8z"
+ android:fillAlpha="0.3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,19.3v-0.9l-3.37,-2.25v-2.47C18.63,13.3 18.35,13 18,13s-0.63,0.3 -0.63,0.68v2.47L14,18.4v0.9l3.37,-1.12v2.48l-0.84,0.68V22L18,21.55L19.47,22v-0.67l-0.84,-0.68v-2.48L22,19.3z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_qs_no_internet_available.xml b/packages/SystemUI/res/drawable/ic_qs_no_internet_available.xml
new file mode 100644
index 0000000..b7cd954
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_no_internet_available.xml
@@ -0,0 +1,31 @@
+<!--
+ 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.
+-->
+<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="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10c0.34,0 0.68,-0.02 1.01,-0.05V20h-1v-0.04c-0.83,-1.2 -1.48,-2.53 -1.91,-3.96H13v-2H9.66c-0.09,-0.66 -0.16,-1.32 -0.16,-2s0.07,-1.35 0.16,-2H21.8C20.87,5.44 16.83,2 11.99,2zM18.92,8h-2.95c-0.32,-1.25 -0.78,-2.45 -1.38,-3.56C16.43,5.07 17.96,6.35 18.92,8zM12,4.04c0.83,1.2 1.48,2.53 1.91,3.96h-3.82C10.52,6.57 11.17,5.24 12,4.04zM4.26,14C4.1,13.36 4,12.69 4,12s0.1,-1.36 0.26,-2h3.38c-0.08,0.66 -0.14,1.32 -0.14,2s0.06,1.34 0.14,2H4.26zM5.08,16h2.95c0.32,1.25 0.78,2.45 1.38,3.56C7.57,18.93 6.04,17.66 5.08,16zM8.03,8H5.08c0.96,-1.66 2.49,-2.93 4.33,-3.56C8.81,5.55 8.35,6.75 8.03,8z"
+ android:fillAlpha="0.3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,14.75c0,-1.93 1.57,-3.5 3.5,-3.5s3.5,1.57 3.5,3.5c0,1.12 -0.69,1.73 -1.36,2.32c-0.64,0.56 -1.26,1.1 -1.26,2.06h-1.75c0,-1.59 0.82,-2.22 1.54,-2.78c0.57,-0.44 1.08,-0.83 1.08,-1.6c0,-0.96 -0.79,-1.75 -1.75,-1.75s-1.75,0.79 -1.75,1.75H16z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.63,20.25h1.75V22h-1.75V20.25z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_qs_no_internet_unavailable.xml b/packages/SystemUI/res/drawable/ic_qs_no_internet_unavailable.xml
new file mode 100644
index 0000000..9fe2b10
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_no_internet_unavailable.xml
@@ -0,0 +1,28 @@
+<!--
+ 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.
+-->
+<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="M2,12C2,6.48 6.47,2 11.99,2C17.52,2 22,6.48 22,12c0,0.34 -0.02,0.67 -0.05,1h-2.02c0.04,-0.33 0.07,-0.66 0.07,-1c0,-0.69 -0.1,-1.36 -0.26,-2h-3.38c0.08,0.66 0.14,1.32 0.14,2c0,0.34 -0.01,0.67 -0.04,1h-2.01c0.03,-0.33 0.05,-0.66 0.05,-1c0,-0.68 -0.07,-1.35 -0.16,-2H9.66c-0.09,0.65 -0.16,1.32 -0.16,2s0.07,1.34 0.16,2H13v2h-2.91c0.43,1.43 1.08,2.76 1.91,3.96V20h1v1.95C12.67,21.98 12.33,22 11.99,22C6.47,22 2,17.52 2,12zM15.97,8h2.95c-0.96,-1.65 -2.49,-2.93 -4.33,-3.56C15.19,5.55 15.65,6.75 15.97,8zM13.91,8C13.48,6.57 12.83,5.24 12,4.04c-0.83,1.2 -1.48,2.53 -1.91,3.96H13.91zM4,12c0,0.69 0.1,1.36 0.26,2h3.38c-0.08,-0.66 -0.14,-1.32 -0.14,-2s0.06,-1.34 0.14,-2H4.26C4.1,10.64 4,11.31 4,12zM8.03,16H5.08c0.96,1.66 2.49,2.93 4.33,3.56C8.81,18.45 8.35,17.25 8.03,16zM5.08,8h2.95c0.32,-1.25 0.78,-2.45 1.38,-3.56C7.57,5.07 6.04,6.34 5.08,8z"
+ android:fillAlpha="0.3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,16.41L20.59,15l-2.09,2.09L16.41,15L15,16.41l2.09,2.09L15,20.59L16.41,22l2.09,-2.08L20.59,22L22,20.59l-2.08,-2.09L22,16.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/layout/people_space_small_avatar_tile.xml b/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml
index 5aa0533..03589d3 100644
--- a/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml
+++ b/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml
@@ -168,6 +168,7 @@
</LinearLayout>
</LinearLayout>
<LinearLayout
+ android:id="@+id/content_background"
android:background="@drawable/people_space_content_background"
android:layout_gravity="center"
android:layout_width="match_parent"
@@ -187,7 +188,8 @@
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:visibility="gone"/>
+ android:visibility="gone"
+ android:scaleType="centerCrop"/>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 0beb286..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nuwe gebruiker"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nie gekoppel nie"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Geen netwerk nie"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi af"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index f56e84a..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"አዲስ ተጠቃሚ"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"በይነመረብ"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"አልተገናኘም"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ምንም አውታረ መረብ የለም"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ጠፍቷል"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index aaaf778..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>
@@ -358,6 +362,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"مستخدم جديد"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"الإنترنت"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ليست متصلة"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"لا تتوفر شبكة"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"إيقاف Wi-Fi"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 7e3e3cb..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>
@@ -353,7 +357,12 @@
<string name="quick_settings_user_title" msgid="8673045967216204537">"ব্যৱহাৰকাৰী"</string>
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"নতুন ব্যৱহাৰকাৰী"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"ৱাই-ফাই"</string>
- <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+ <string name="quick_settings_internet_label" msgid="6603068555872455463">"ইণ্টাৰনেট"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
<skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"সংযোগ হৈ থকা নাই"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"নেটৱৰ্ক নাই"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 6c22cc3..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Yeni istifadəçi"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"İnternet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Bağlantı yoxdur"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Şəbəkə yoxdur"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi sönülüdür"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 2f3d6d2..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>
@@ -355,6 +359,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Veza nije uspostavljena"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nema mreže"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi je isključen"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 925e86a..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>
@@ -356,6 +360,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Новы карыстальнік"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Інтэрнэт"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Няма падключэння"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Няма сеткi"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi адключаны"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 6a683c7..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Нов потребител"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Няма връзка"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Няма мрежа"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi е изключен"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 953ec00..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,6 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"নতুন ব্যবহারকারী"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"ওয়াই-ফাই"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"ইন্টারনেট"</string>
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"বিমানের জন্য নিরাপদ"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"নেটওয়ার্ক উপলভ্য"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"নেটওয়ার্ক উপলভ্য নেই"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"সংযুক্ত নয়"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"কোনো নেটওয়ার্ক নেই"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ওয়াই-ফাই বন্ধ"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 6bf1852..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,6 +359,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Sigurno za rad u zrakoplovu"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Mreže su dostupne"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Mreže nisu dostupne"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nije povezano"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nema mreže"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi je isključen"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 71105cc..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Usuari nou"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Desconnectat"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No hi ha cap xarxa"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desconnectada"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 65e6767..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>
@@ -356,6 +360,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nový uživatel"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nepřipojeno"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Žádná síť"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi vypnuta"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index e50aa57..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Ny bruger"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ikke forbundet"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Intet netværk"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi slået fra"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 3cdebf9..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Neuer Nutzer"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"WLAN"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nicht verbunden"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Kein Netz"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WLAN aus"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index c6ae7f4..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,6 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Νέος χρήστης"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Διαδίκτυο"</string>
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Ασφαλές για πτήση"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Υπάρχουν διαθέσιμα δίκτυα"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Δεν υπάρχουν διαθέσιμα δίκτυα"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Μη συνδεδεμένο"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Κανένα δίκτυο"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ανενεργό"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index ea25ec6..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index a373a5c..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index ea25ec6..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index ea25ec6..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 101d112..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,6 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Airplane-safe"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Networks available"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Networks unavailable"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 1fc1609..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Usuario nuevo"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Sin conexión"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sin red"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desactivada"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index dd36a64..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nuevo usuario"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"No conectado"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No hay red."</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desactivado"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index fb27be9..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Uus kasutaja"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ühendus puudub"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Võrku pole"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi-ühendus on väljas"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index d8b21c6..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Erabiltzaile berria"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wifia"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Konektatu gabe"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ez dago sarerik"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi konexioa desaktibatuta"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index df306f8..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,6 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"کاربر جدید"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"اینترنت"</string>
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"ایمن در هواپیما"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"شبکه دردسترس است"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"شبکه دردسترس نیست"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"متصل نیست"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"شبکهای موجود نیست"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi خاموش است"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 4e0af37..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Uusi käyttäjä"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ei yhteyttä"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ei verkkoa"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi-yhteys pois käytöstä"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 8c944a4..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,6 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nouvel utilisateur"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Sécuritaire pour les avions"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Réseaux accessibles"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Aucun réseau accessible"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non connecté"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Aucun réseau"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi désactivé"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 7ad239d..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nouvel utilisateur"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non connecté"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Aucun réseau"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi désactivé"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 145b3c0..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo usuario"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wifi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non conectada"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Non hai rede"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wifi desactivada"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index ea7af17..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"નવો વપરાશકર્તા"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"વાઇ-ફાઇ"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"ઇન્ટરનેટ"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"કનેક્ટ થયેલ નથી"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"કોઈ નેટવર્ક નથી"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"વાઇ-ફાઇ બંધ"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index a8bed5b..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>
@@ -356,6 +360,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"नया उपयोगकर्ता"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"वाई-फ़ाई"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"इंटरनेट"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"कनेक्ट नहीं है"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"कोई नेटवर्क नहीं"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"वाई-फ़ाई बंद"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 4ffa5b2..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,6 +359,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Sigurno za rad u zrakoplovu"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Mreže su dostupne"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Mreže nisu dostupne"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nije povezano"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nema mreže"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi isključen"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index cff5b0d..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Új felhasználó"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nincs kapcsolat"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nincs hálózat"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi kikapcsolva"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 91ffe73..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,6 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Նոր օգտատեր"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Ինտերնետ"</string>
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Ցանցեր, որոնք անվտանգ են ինքնաթիռում"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Հասանելի ցանցեր"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Անհասանելի ցանցեր"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Միացված չէ"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ցանց չկա"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi-ը անջատված է"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index f9e7397..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Pengguna baru"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Tidak Terhubung"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tidak Ada Jaringan"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Mati"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index db5dac43..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nýr notandi"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Engin tenging"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ekkert net"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Slökkt á Wi-Fi"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 86e65bb..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nuovo utente"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non connessa"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nessuna rete"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi disattivato"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 8548dae..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>
@@ -356,6 +360,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"משתמש חדש"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"אינטרנט"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"אין חיבור"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"אין רשת"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi כבוי"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 6cd5608..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"新しいユーザー"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"インターネット"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"接続されていません"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ネットワークなし"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi OFF"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 52d9f0e..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,6 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"ახალი მომხმარებელი"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"ინტერნეტი"</string>
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"თვითმფრინავისთვის უსაფრთხო"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"ქსელები ხელმისაწვდომია"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"ქსელები მიუწვდომელია"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"არ არის დაკავშირებული."</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ქსელი არ არის"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi გამორთულია"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index fcf8743..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Жаңа пайдаланушы"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Жалғанбаған"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Желі жоқ"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi өшірулі"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index e92bf9b..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"អ្នកប្រើថ្មី"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"អ៊ីនធឺណិត"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"មិនបានតភ្ជាប់"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"គ្មានបណ្ដាញ"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"វ៉ាយហ្វាយបានបិទ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index d7a0adc..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"ಹೊಸ ಬಳಕೆದಾರರು"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"ವೈ-ಫೈ"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"ಇಂಟರ್ನೆಟ್"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ಸಂಪರ್ಕಗೊಂಡಿಲ್ಲ"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ನೆಟ್ವರ್ಕ್ ಇಲ್ಲ"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ವೈ-ಫೈ ಆಫ್"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 54bcfb9..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"신규 사용자"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"인터넷"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"연결되어 있지 않음"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"네트워크가 연결되지 않음"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi 꺼짐"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index d513380..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>
@@ -356,6 +360,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Жаңы колдонуучу"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Байланышкан жок"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Желе жок"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi өчүк"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 7e595cd..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,6 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"ຜູ່ໃຊ້ໃໝ່"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"ອິນເຕີເນັດ"</string>
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"ປອດໄພກັບໃນຍົນ"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"ມີເຄືອຂ່າຍທີ່ສາມາດໃຊ້ໄດ້"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"ບໍ່ມີເຄືອຂ່າຍທີ່ສາມາດໃຊ້ໄດ້"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ບໍ່ໄດ້ເຊື່ອມຕໍ່"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ບໍ່ມີເຄືອຂ່າຍ"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ປິດ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index e0c27a9..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>
@@ -356,6 +360,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Naujas naudotojas"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internetas"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Neprisijungta"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tinklo nėra"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"„Wi-Fi“ išjungta"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index ff36f51..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>
@@ -355,6 +359,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Jauns lietotājs"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internets"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nav izveidots savienojums"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nav tīkla"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ir izslēgts"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 203d8b9..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Нов корисник"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Не е поврзано"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Нема мрежа"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi е исклучено"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 77925ae..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"പുതിയ ഉപയോക്താവ്"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"വൈഫൈ"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"ഇന്റർനെറ്റ്"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"കണക്റ്റ് ചെയ്തിട്ടില്ല"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"നെറ്റ്വർക്ക് ഒന്നുമില്ല"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"വൈഫൈ ഓഫുചെയ്യുക"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index c772294..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Шинэ хэрэглэгч"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернэт"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Холбогдоогүй"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Сүлжээгүй"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi унтарсан"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 965b895..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>
@@ -353,7 +357,12 @@
<string name="quick_settings_user_title" msgid="8673045967216204537">"वापरकर्ता"</string>
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"नवीन वापरकर्ता"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"वाय-फाय"</string>
- <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+ <string name="quick_settings_internet_label" msgid="6603068555872455463">"इंटरनेट"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
<skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"कनेक्ट केले नाही"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"नेटवर्क नाही"</string>
@@ -456,11 +465,9 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"प्रोफाईल दर्शवा"</string>
<string name="user_add_user" msgid="4336657383006913022">"वापरकर्ता जोडा"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"नवीन वापरकर्ता"</string>
- <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
- <skip />
+ <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"अतिथी सत्र संपायचे का?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"या सत्रातील सर्व अॅप्स आणि डेटा हटवला जाईल."</string>
- <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
- <skip />
+ <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"सत्र संपवा"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"अतिथी, तुमचे पुन्हा स्वागत आहे!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"तुम्ही तुमचे सत्र सुरू ठेवू इच्छिता?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"येथून सुरू करा"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index d3e3601..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Pengguna baharu"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Tidak Disambungkan"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tiada Rangkaian"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Dimatikan"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 0c1f9cb..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"အသုံးပြုသူ အသစ်"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"အင်တာနက်"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ချိတ်ဆက်မထားပါ"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ကွန်ရက်မရှိပါ"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ဝိုင်ဖိုင်ပိတ်ရန်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 565e7fe..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Ny bruker"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internett"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ikke tilkoblet"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ingen nettverk"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi er av"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 9d95cf0..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"नयाँ प्रयोगकर्ता"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"इन्टरनेट"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"जोडिएको छैन"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"नेटवर्क छैन"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi बन्द"</string>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 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 f598fa3..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nieuwe gebruiker"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wifi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Niet verbonden"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Geen netwerk"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wifi uit"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 1e07dd4..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>
@@ -353,7 +357,12 @@
<string name="quick_settings_user_title" msgid="8673045967216204537">"ୟୁଜର୍"</string>
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"ୱାଇ-ଫାଇ"</string>
- <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+ <string name="quick_settings_internet_label" msgid="6603068555872455463">"ଇଣ୍ଟରନେଟ୍"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
<skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ସଂଯୁକ୍ତ ହୋଇନାହିଁ"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ନେଟ୍ୱର୍କ ନାହିଁ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 7d5bc96..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>
@@ -353,7 +357,12 @@
<string name="quick_settings_user_title" msgid="8673045967216204537">"ਵਰਤੋਂਕਾਰ"</string>
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"ਵਾਈ-ਫਾਈ"</string>
- <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+ <string name="quick_settings_internet_label" msgid="6603068555872455463">"ਇੰਟਰਨੈੱਟ"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
<skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ਕੋਈ ਨੈੱਟਵਰਕ ਨਹੀਂ"</string>
@@ -456,11 +465,9 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ਪ੍ਰੋਫਾਈਲ ਦਿਖਾਓ"</string>
<string name="user_add_user" msgid="4336657383006913022">"ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ"</string>
- <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
- <skip />
+ <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ਕੀ ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਸਮਾਪਤ ਕਰਨਾ ਹੈ?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ਇਸ ਸੈਸ਼ਨ ਵਿੱਚ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਜਾਏਗਾ।"</string>
- <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
- <skip />
+ <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ਸੈਸ਼ਨ ਸਮਾਪਤ ਕਰੋ"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"ਮਹਿਮਾਨ, ਫਿਰ ਤੁਹਾਡਾ ਸੁਆਗਤ ਹੈ!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"ਕੀ ਤੁਸੀਂ ਆਪਣਾ ਸੈਸ਼ਨ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ਸ਼ੁਰੂ ਕਰੋ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 561e70d..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>
@@ -356,6 +360,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nowy użytkownik"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Brak połączenia"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Brak sieci"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi wyłączone"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 7fe53d3..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,6 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo usuário"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Segura para aviões"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Redes disponíveis"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Redes indisponíveis"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Não conectado"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sem rede"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desligado"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index dd154e8..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,6 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo utilizador"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Seguras para aviões"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Redes disponíveis"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Redes indisponíveis"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Não Ligado"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sem Rede"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Desligado"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 7fe53d3..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,6 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo usuário"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Segura para aviões"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Redes disponíveis"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Redes indisponíveis"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Não conectado"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sem rede"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desligado"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 317fc09..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>
@@ -355,6 +359,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Utilizator nou"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Neconectată"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nicio rețea"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi deconectat"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index bbb014e..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>
@@ -356,6 +360,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Новый пользователь"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Нет соединения"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Нет сети"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi выкл."</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index a7a2bb7..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"නව පරිශීලකයා"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"අන්තර්ජාලය"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"සම්බන්ධ වී නොමැත"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ජාලයක් නැත"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi අක්රියයි"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 7eb5297..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>
@@ -356,6 +360,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nový používateľ"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi‑Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nepripojené"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Žiadna sieť"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Sieť Wi‑Fi je vypnutá"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 6d23f26..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>
@@ -356,6 +360,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nov uporabnik"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Povezava ni vzpostavljena"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ni omrežja"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi izklopljen"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 7e89b93..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Përdorues i ri"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nuk është i lidhur"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nuk ka rrjet"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi është i çaktivizuar"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index af5175c..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>
@@ -355,6 +359,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Нови корисник"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Веза није успостављена"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Нема мреже"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi је искључен"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 41bca7c..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Ny användare"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ej ansluten"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Inget nätverk"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi av"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index f122013..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,6 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Mtumiaji mpya"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Intaneti"</string>
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Hali salama ya ndegeni"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Mitandao inapatikana"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Mitandao haipatikani"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Haijaunganishwa"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Hakuna Mtandao"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Imezimwa"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index c076a03..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"புதியவர்"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"வைஃபை"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"இணையம்"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"இணைக்கப்படவில்லை"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"நெட்வொர்க் இல்லை"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"வைஃபையை முடக்கு"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index fca5107..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"కొత్త వినియోగదారు"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"ఇంటర్నెట్"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"కనెక్ట్ చేయబడలేదు"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"నెట్వర్క్ లేదు"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ఆఫ్లో ఉంది"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index c909d37..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"ผู้ใช้ใหม่"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"อินเทอร์เน็ต"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ไม่ได้เชื่อมต่อ"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ไม่มีเครือข่าย"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ปิด WiFi"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 2e75fa3..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Bagong user"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Hindi Nakakonekta"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Walang Network"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Naka-off ang Wi-Fi"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 98552b1..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Yeni kullanıcı"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Kablosuz"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"İnternet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Bağlı Değil"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ağ yok"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Kablosuz Kapalı"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index b70d36a..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>
@@ -356,6 +360,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Новий користувач"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Інтернет"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Не під’єднано."</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Немає мережі"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi вимкнено"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index bbadf6a..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"نیا صارف"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"انٹرنیٹ"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"مربوط نہیں ہے"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"کوئی نیٹ ورک نہیں ہے"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi آف ہے"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index a20676d..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,6 +358,9 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Yangi foydalanuvchi"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <string name="quick_settings_airplane_safe_label" msgid="2665758539772645899">"Samolyot uchun xavfsiz"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Tarmoqlar mavjud"</string>
+ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Tarmoqqa ulanish imkonsiz"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ulanmagan"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tarmoq mavjud emas"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi o‘chiq"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 5de7df6a..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Người dùng mới"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Chưa được kết nối"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Không có mạng nào"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Tắt Wi-Fi"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 7f3f394..55b524e 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -63,9 +63,9 @@
<string name="usb_debugging_allow" msgid="1722643858015321328">"允许"</string>
<string name="usb_debugging_secondary_user_title" msgid="7843050591380107998">"不允许使用 USB 调试功能"</string>
<string name="usb_debugging_secondary_user_message" msgid="3740347841470403244">"目前已登录此设备的用户无法开启 USB 调试功能。要使用此功能,请切换为主要用户的帐号。"</string>
- <string name="wifi_debugging_title" msgid="7300007687492186076">"要允许在此网络上进行无线调试吗?"</string>
+ <string name="wifi_debugging_title" msgid="7300007687492186076">"要允许通过此网络上进行无线调试吗?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"网络名称 (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWLAN 地址 (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
- <string name="wifi_debugging_always" msgid="2968383799517975155">"在此网络上始终允许"</string>
+ <string name="wifi_debugging_always" msgid="2968383799517975155">"始终允许通过此网络进行调试"</string>
<string name="wifi_debugging_allow" msgid="4573224609684957886">"允许"</string>
<string name="wifi_debugging_secondary_user_title" msgid="2493201475880517725">"不允许使用无线调试功能"</string>
<string name="wifi_debugging_secondary_user_message" msgid="4492383073970079751">"目前已登录此设备的用户无法开启无线调试功能。要使用此功能,请切换为主要用户的帐号。"</string>
@@ -344,6 +344,10 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"输入法"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"位置信息"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"位置信息:关闭"</string>
+ <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
+ <skip />
+ <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
+ <skip />
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"媒体设备"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"只能拨打紧急呼救电话"</string>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"新用户"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"WLAN"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"互联网"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"未连接"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"无网络"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WLAN:关闭"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 485f1cf..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"新使用者"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"互聯網"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"未連線"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"沒有網絡"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi 關閉"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 1edeacd..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"新使用者"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"網際網路"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"未連線"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"沒有網路"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi 已關閉"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 58baebc..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>
@@ -354,6 +358,12 @@
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Umsebenzisi omusha"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"I-Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"I-inthanethi"</string>
+ <!-- no translation found for quick_settings_airplane_safe_label (2665758539772645899) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_available (1875138606855420438) -->
+ <skip />
+ <!-- no translation found for quick_settings_networks_unavailable (1167847013337940082) -->
+ <skip />
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Akuxhunyiwe"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ayikho inethiwekhi"</string>
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"I-Wi-Fi icimile"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 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 86af464..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] -->
@@ -841,6 +845,12 @@
<string name="quick_settings_wifi_label">Wi-Fi</string>
<!-- QuickSettings: Internet [CHAR LIMIT=NONE] -->
<string name="quick_settings_internet_label">Internet</string>
+ <!-- QuickSettings: Airplane-safe [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_airplane_safe_label">Airplane-safe</string>
+ <!-- QuickSettings: networks available [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_networks_available">Networks available</string>
+ <!-- QuickSettings: networks unavailable [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_networks_unavailable">Networks unavailable</string>
<!-- QuickSettings: Wifi (Not connected) [CHAR LIMIT=NONE] -->
<string name="quick_settings_wifi_not_connected">Not Connected</string>
<!-- QuickSettings: Wifi (No network) [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/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index bdfc65e..937c1df 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -90,6 +90,10 @@
public static final int SYSUI_STATE_GLOBAL_ACTIONS_SHOWING = 1 << 15;
// The one-handed mode is active
public static final int SYSUI_STATE_ONE_HANDED_ACTIVE = 1 << 16;
+ // Allow system gesture no matter the system bar(s) is visible or not
+ public static final int SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY = 1 << 17;
+ // The IME is showing
+ public static final int SYSUI_STATE_IME_SHOWING = 1 << 18;
@Retention(RetentionPolicy.SOURCE)
@IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -107,8 +111,10 @@
SYSUI_STATE_TRACING_ENABLED,
SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED,
SYSUI_STATE_BUBBLES_EXPANDED,
+ SYSUI_STATE_GLOBAL_ACTIONS_SHOWING,
SYSUI_STATE_ONE_HANDED_ACTIVE,
- SYSUI_STATE_GLOBAL_ACTIONS_SHOWING
+ SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
+ SYSUI_STATE_IME_SHOWING
})
public @interface SystemUiStateFlags {}
@@ -133,6 +139,9 @@
? "asst_gesture_constrain" : "");
str.add((flags & SYSUI_STATE_BUBBLES_EXPANDED) != 0 ? "bubbles_expanded" : "");
str.add((flags & SYSUI_STATE_ONE_HANDED_ACTIVE) != 0 ? "one_handed_active" : "");
+ str.add((flags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0
+ ? "allow_gesture" : "");
+ str.add((flags & SYSUI_STATE_IME_SHOWING) != 0 ? "ime_visible" : "");
return str.toString();
}
@@ -175,6 +184,9 @@
* disabled.
*/
public static boolean isAssistantGestureDisabled(int sysuiStateFlags) {
+ if ((sysuiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0) {
+ sysuiStateFlags &= ~SYSUI_STATE_NAV_BAR_HIDDEN;
+ }
// Disable when in quick settings, screen pinning, immersive, the bouncer is showing,
// or search is disabled
int disableFlags = SYSUI_STATE_SCREEN_PINNING
@@ -205,6 +217,9 @@
|| (sysuiStateFlags & SYSUI_STATE_GLOBAL_ACTIONS_SHOWING) != 0) {
return false;
}
+ if ((sysuiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0) {
+ sysuiStateFlags &= ~SYSUI_STATE_NAV_BAR_HIDDEN;
+ }
// Disable when in immersive, or the notifications are interactive
int disableFlags = SYSUI_STATE_NAV_BAR_HIDDEN
| SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
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/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 7c6a274..59e81cf 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -87,8 +87,7 @@
private void initColors() {
mLockScreenColors[0] = Utils.getColorAttrDefaultColor(getContext(),
com.android.systemui.R.attr.wallpaperTextColor);
- mLockScreenColors[1] = Utils.getColorAttrDefaultColor(getContext(),
- com.android.systemui.R.attr.wallpaperTextColorSecondary);
+ mLockScreenColors[1] = mLockScreenColors[0]; // same color
mView.setColors(mDozingColors, mLockScreenColors);
mView.animateDoze(mIsDozing, false);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
index ca99563..62d3093 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
@@ -38,7 +38,7 @@
* The time's text color is a gradient that changes its colors based on its controller.
*/
public class AnimatableClockView extends TextView {
- private static final CharSequence FORMAT_12_HOUR = "hh\nmm";
+ private static final CharSequence FORMAT_12_HOUR = "h\nmm";
private static final CharSequence FORMAT_24_HOUR = "HH\nmm";
private static final long ANIM_DURATION = 300;
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/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index eea168a..39cbc90 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -26,9 +26,9 @@
import android.os.Message
import android.os.UserHandle
import android.text.TextUtils
+import android.util.IndentingPrintWriter
import android.util.SparseArray
import com.android.internal.annotations.VisibleForTesting
-import com.android.internal.util.IndentingPrintWriter
import com.android.systemui.Dumpable
import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
import com.android.systemui.dump.DumpManager
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 5d184ef..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,8 @@
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;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -90,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;
@@ -158,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. */
@@ -208,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;
@@ -488,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;
@@ -628,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);
@@ -817,6 +827,7 @@
mNavigationBarView.setNavigationIconHints(hints);
}
checkBarModes();
+ updateSystemUiStateFlags(-1);
}
@Override
@@ -887,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;
}
@@ -904,6 +916,10 @@
mLightBarController.onNavigationBarAppearanceChanged(appearance, nbModeChanged,
mNavigationBarMode, navbarColorManagedByIme);
}
+ if (mBehavior != behavior) {
+ mBehavior = behavior;
+ updateSystemUiStateFlags(-1);
+ }
}
@Override
@@ -1315,6 +1331,10 @@
mSysUiFlagsContainer.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable)
.setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable)
.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);
@@ -1417,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/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index 065920c..a8761a6 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -80,6 +80,7 @@
INVALID_APPWIDGET_ID);
mShowSingleConversation = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
+ setResult(RESULT_CANCELED);
// Finish the configuration activity immediately if a widget is added for multiple
// conversations. If the mAppWidgetId is INVALID, then the activity wasn't launched as a
// widget configuration activity.
@@ -151,10 +152,14 @@
private void finishActivity() {
if (PeopleSpaceUtils.DEBUG) Log.d(TAG, "Widget added!");
mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_ADDED);
+ setActivityResult(RESULT_OK);
+ finish();
+ }
+
+ private void setActivityResult(int result) {
Intent resultValue = new Intent();
resultValue.putExtra(EXTRA_APPWIDGET_ID, mAppWidgetId);
- setResult(RESULT_OK, resultValue);
- finish();
+ setResult(result, resultValue);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 67d53fc..8669a81 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -294,6 +294,7 @@
)
);
views.setImageViewIcon(R.id.person_icon, tile.getUserIcon());
+ views.setBoolean(R.id.content_background, "setClipToOutline", true);
Intent activityIntent = new Intent(context, LaunchConversationActivity.class);
activityIntent.addFlags(
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
index 1d2e747..eec69f98 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
@@ -28,7 +28,7 @@
appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
.toList()
.sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps
- { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest)
+ { it.second.minOrNull() })) // Sort by "smallest" AppOpp (Location is largest)
types = itemsList.map { it.privacyType }.distinct().sorted()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 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/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 86524f5..17866e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -58,6 +58,8 @@
/** Quick settings tile: Internet **/
public class InternetTile extends QSTileImpl<SignalState> {
private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS);
+ private static final Intent INTERNET_PANEL =
+ new Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
protected final NetworkController mController;
private final DataUsageController mDataController;
@@ -102,7 +104,7 @@
@Override
protected void handleClick() {
- mActivityStarter.postStartActivityDismissingKeyguard(WIFI_SETTINGS, 0);
+ mActivityStarter.postStartActivityDismissingKeyguard(INTERNET_PANEL, 0);
}
@Override
@@ -138,6 +140,7 @@
}
private static final class WifiCallbackInfo {
+ boolean mAirplaneModeEnabled;
boolean mEnabled;
boolean mConnected;
int mWifiSignalIconId;
@@ -147,11 +150,15 @@
String mWifiSignalContentDescription;
boolean mIsTransient;
public String mStatusLabel;
+ boolean mNoDefaultNetwork;
+ boolean mNoValidatedNetwork;
+ boolean mNoNetworksAvailable;
@Override
public String toString() {
return new StringBuilder("WifiCallbackInfo[")
- .append("mEnabled=").append(mEnabled)
+ .append("mAirplaneModeEnabled=").append(mAirplaneModeEnabled)
+ .append(",mEnabled=").append(mEnabled)
.append(",mConnected=").append(mConnected)
.append(",mWifiSignalIconId=").append(mWifiSignalIconId)
.append(",mSsid=").append(mSsid)
@@ -159,6 +166,9 @@
.append(",mActivityOut=").append(mActivityOut)
.append(",mWifiSignalContentDescription=").append(mWifiSignalContentDescription)
.append(",mIsTransient=").append(mIsTransient)
+ .append(",mNoDefaultNetwork=").append(mNoDefaultNetwork)
+ .append(",mNoValidatedNetwork=").append(mNoValidatedNetwork)
+ .append(",mNoNetworksAvailable=").append(mNoNetworksAvailable)
.append(']').toString();
}
}
@@ -173,6 +183,9 @@
boolean mNoSim;
boolean mRoaming;
boolean mMultipleSubs;
+ boolean mNoDefaultNetwork;
+ boolean mNoValidatedNetwork;
+ boolean mNoNetworksAvailable;
@Override
public String toString() {
@@ -186,6 +199,9 @@
.append(",mNoSim=").append(mNoSim)
.append(",mRoaming=").append(mRoaming)
.append(",mMultipleSubs=").append(mMultipleSubs)
+ .append(",mNoDefaultNetwork=").append(mNoDefaultNetwork)
+ .append(",mNoValidatedNetwork=").append(mNoValidatedNetwork)
+ .append(",mNoNetworksAvailable=").append(mNoNetworksAvailable)
.append(']').toString();
}
}
@@ -198,7 +214,7 @@
public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
boolean activityIn, boolean activityOut, String description, boolean isTransient,
String statusLabel) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
+ if (DEBUG) {
Log.d(TAG, "setWifiIndicators: "
+ "enabled = " + enabled + ","
+ "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + ","
@@ -209,13 +225,20 @@
+ "isTransient = " + isTransient + ","
+ "statusLabel = " + statusLabel);
}
+ // When airplane mode is enabled, we need to refresh the Internet Tile even if the WiFi
+ // is not the default network.
+ if (qsIcon == null && !mWifiInfo.mAirplaneModeEnabled) {
+ return;
+ }
+ if (qsIcon != null) {
+ mWifiInfo.mConnected = qsIcon.visible;
+ mWifiInfo.mWifiSignalIconId = qsIcon.icon;
+ mWifiInfo.mWifiSignalContentDescription = qsIcon.contentDescription;
+ }
mWifiInfo.mEnabled = enabled;
- mWifiInfo.mConnected = qsIcon.visible;
- mWifiInfo.mWifiSignalIconId = qsIcon.icon;
mWifiInfo.mSsid = description;
mWifiInfo.mActivityIn = activityIn;
mWifiInfo.mActivityOut = activityOut;
- mWifiInfo.mWifiSignalContentDescription = qsIcon.contentDescription;
mWifiInfo.mIsTransient = isTransient;
mWifiInfo.mStatusLabel = statusLabel;
refreshState(mWifiInfo);
@@ -227,7 +250,7 @@
CharSequence typeContentDescription,
CharSequence typeContentDescriptionHtml, CharSequence description,
boolean isWide, int subId, boolean roaming) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
+ if (DEBUG) {
Log.d(TAG, "setMobileDataIndicators: "
+ "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + ","
+ "qsIcon = " + (qsIcon == null ? "" : qsIcon.toString()) + ","
@@ -246,7 +269,8 @@
// Not data sim, don't display.
return;
}
- mCellularInfo.mDataSubscriptionName = mController.getMobileDataNetworkName();
+ mCellularInfo.mDataSubscriptionName =
+ description == null ? mController.getMobileDataNetworkName() : description;
mCellularInfo.mDataContentDescription =
(description != null) ? typeContentDescriptionHtml : null;
mCellularInfo.mMobileSignalIconId = qsIcon.icon;
@@ -259,7 +283,7 @@
@Override
public void setNoSims(boolean show, boolean simDetected) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
+ if (DEBUG) {
Log.d(TAG, "setNoSims: "
+ "show = " + show + ","
+ "simDetected = " + simDetected);
@@ -274,18 +298,36 @@
@Override
public void setIsAirplaneMode(IconState icon) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "InternetTile-setIsAirplaneMode: "
+ if (DEBUG) {
+ Log.d(TAG, "setIsAirplaneMode: "
+ "icon = " + (icon == null ? "" : icon.toString()));
}
mCellularInfo.mAirplaneModeEnabled = icon.visible;
+ mWifiInfo.mAirplaneModeEnabled = icon.visible;
refreshState(mCellularInfo);
}
+
+ @Override
+ public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork,
+ boolean noNetworksAvailable) {
+ if (DEBUG) {
+ Log.d(TAG, "setConnectivityStatus: "
+ + "noDefaultNetwork = " + noDefaultNetwork + ","
+ + "noValidatedNetwork = " + noValidatedNetwork + ","
+ + "noNetworksAvailable = " + noNetworksAvailable);
+ }
+ mCellularInfo.mNoDefaultNetwork = noDefaultNetwork;
+ mCellularInfo.mNoValidatedNetwork = noValidatedNetwork;
+ mCellularInfo.mNoNetworksAvailable = noNetworksAvailable;
+ mWifiInfo.mNoDefaultNetwork = noDefaultNetwork;
+ mWifiInfo.mNoValidatedNetwork = noValidatedNetwork;
+ mWifiInfo.mNoNetworksAvailable = noNetworksAvailable;
+ refreshState(mWifiInfo);
+ }
}
@Override
protected void handleUpdateState(SignalState state, Object arg) {
- Log.d(TAG, "handleUpdateState: " + "arg = " + arg);
if (arg instanceof CellularCallbackInfo) {
mLastTileState = 0;
handleUpdateCellularState(state, arg);
@@ -306,6 +348,9 @@
private void handleUpdateWifiState(SignalState state, Object arg) {
WifiCallbackInfo cb = (WifiCallbackInfo) arg;
+ if (DEBUG) {
+ Log.d(TAG, "handleUpdateWifiState: " + "WifiCallbackInfo = " + cb.toString());
+ }
boolean wifiConnected = cb.mEnabled && (cb.mWifiSignalIconId > 0) && (cb.mSsid != null);
boolean wifiNotConnected = (cb.mWifiSignalIconId > 0) && (cb.mSsid == null);
boolean enabledChanging = state.value != cb.mEnabled;
@@ -326,25 +371,44 @@
final StringBuffer minimalContentDescription = new StringBuffer();
final StringBuffer minimalStateDescription = new StringBuffer();
final Resources r = mContext.getResources();
- // TODO(b/174753536): Use the new "Internet" string as state.label once available.
- if (cb.mIsTransient) {
+ state.label = r.getString(R.string.quick_settings_internet_label);
+ if (cb.mAirplaneModeEnabled) {
+ if (!state.value) {
+ state.state = Tile.STATE_UNAVAILABLE;
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_airplane);
+ state.secondaryLabel = r.getString(R.string.status_bar_airplane);
+ } else if (!wifiConnected) {
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_airplane);
+ if (cb.mNoNetworksAvailable) {
+ state.secondaryLabel =
+ r.getString(R.string.quick_settings_networks_unavailable);
+ } else {
+ state.secondaryLabel =
+ r.getString(R.string.quick_settings_networks_available);
+ }
+ } else {
+ state.icon = ResourceIcon.get(cb.mWifiSignalIconId);
+ state.label = r.getString(R.string.quick_settings_airplane_safe_label);
+ }
+ } else if (cb.mNoDefaultNetwork && cb.mNoNetworksAvailable) {
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
+ state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable);
+ } else if (cb.mNoValidatedNetwork && !cb.mNoNetworksAvailable) {
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available);
+ state.secondaryLabel = r.getString(R.string.quick_settings_networks_available);
+ } else if (cb.mIsTransient) {
state.icon = ResourceIcon.get(
com.android.internal.R.drawable.ic_signal_wifi_transient_animation);
- state.label = r.getString(R.string.quick_settings_internet_label);
} else if (!state.value) {
state.slash.isSlashed = true;
state.state = Tile.STATE_INACTIVE;
state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_DISABLED);
- state.label = r.getString(R.string.quick_settings_internet_label);
} else if (wifiConnected) {
state.icon = ResourceIcon.get(cb.mWifiSignalIconId);
- state.label = r.getString(R.string.quick_settings_internet_label);
} else if (wifiNotConnected) {
state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK);
- state.label = r.getString(R.string.quick_settings_internet_label);
} else {
state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK);
- state.label = r.getString(R.string.quick_settings_internet_label);
}
minimalContentDescription.append(
mContext.getString(R.string.quick_settings_internet_label)).append(",");
@@ -366,34 +430,37 @@
private void handleUpdateCellularState(SignalState state, Object arg) {
CellularCallbackInfo cb = (CellularCallbackInfo) arg;
+ if (DEBUG) {
+ Log.d(TAG, "handleUpdateCellularState: " + "CellularCallbackInfo = " + cb.toString());
+ }
final Resources r = mContext.getResources();
// TODO(b/174753536): Use the new "Internet" string as state.label once available.
state.label = r.getString(R.string.quick_settings_internet_label);
+ state.state = Tile.STATE_ACTIVE;
boolean mobileDataEnabled = mDataController.isMobileDataSupported()
&& mDataController.isMobileDataEnabled();
state.value = mobileDataEnabled;
state.activityIn = mobileDataEnabled && cb.mActivityIn;
state.activityOut = mobileDataEnabled && cb.mActivityOut;
state.expandedAccessibilityClassName = Switch.class.getName();
- if (cb.mNoSim) {
- state.icon = ResourceIcon.get(R.drawable.ic_qs_no_sim);
- } else {
- state.icon = new SignalIcon(cb.mMobileSignalIconId);
- }
- if (cb.mNoSim) {
+ if (cb.mAirplaneModeEnabled && cb.mNoDefaultNetwork) {
state.state = Tile.STATE_UNAVAILABLE;
- state.secondaryLabel = r.getString(R.string.keyguard_missing_sim_message_short);
- } else if (cb.mAirplaneModeEnabled) {
- state.state = Tile.STATE_UNAVAILABLE;
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_airplane);
state.secondaryLabel = r.getString(R.string.status_bar_airplane);
- } else if (mobileDataEnabled) {
- state.state = Tile.STATE_ACTIVE;
+ } else if (cb.mNoDefaultNetwork && cb.mNoNetworksAvailable) {
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
+ state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable);
+ } else if (cb.mNoValidatedNetwork && !cb.mNoNetworksAvailable) {
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available);
+ state.secondaryLabel = r.getString(R.string.quick_settings_networks_available);
+ } else {
+ if (cb.mAirplaneModeEnabled) {
+ state.label = r.getString(R.string.quick_settings_airplane_safe_label);
+ }
+ state.icon = new SignalIcon(cb.mMobileSignalIconId);
state.secondaryLabel = appendMobileDataType(cb.mDataSubscriptionName,
getMobileDataContentName(cb));
- } else {
- state.state = Tile.STATE_INACTIVE;
- state.secondaryLabel = r.getString(R.string.cell_data_off);
}
state.contentDescription = state.label;
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/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 19eac77..a6fd011 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -307,6 +307,9 @@
boolean activityIn, boolean activityOut, String description, boolean isTransient,
String statusLabel) {
if (DEBUG) Log.d(TAG, "onWifiSignalChanged enabled=" + enabled);
+ if (qsIcon == null) {
+ return;
+ }
mInfo.enabled = enabled;
mInfo.connected = qsIcon.visible;
mInfo.wifiSignalIconId = qsIcon.icon;
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 357702a..211f507 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -63,6 +63,7 @@
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
+import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
@@ -73,7 +74,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
-import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ShareTransition;
+import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
import com.android.systemui.shared.system.QuickStepContract;
import java.util.ArrayList;
@@ -105,7 +106,6 @@
private static final long SCREENSHOT_DISMISS_Y_DURATION_MS = 350;
private static final long SCREENSHOT_DISMISS_ALPHA_DURATION_MS = 183;
private static final long SCREENSHOT_DISMISS_ALPHA_OFFSET_MS = 50; // delay before starting fade
- private static final long SCREENSHOT_DISMISS_SHARE_OFFSET_MS = 300; // delay after share clicked
private static final float SCREENSHOT_ACTIONS_START_SCALE_X = .7f;
private static final float ROUNDED_CORNER_RADIUS = .05f;
private static final int SWIPE_PADDING_DP = 12; // extra padding around views to allow swipe
@@ -117,6 +117,7 @@
private final DisplayMetrics mDisplayMetrics;
private final float mCornerSizeX;
private final float mDismissDeltaY;
+ private final AccessibilityManager mAccessibilityManager;
private int mNavMode;
private int mLeftInset;
@@ -140,7 +141,7 @@
private UiEventLogger mUiEventLogger;
private ScreenshotViewCallback mCallbacks;
private Animator mDismissAnimation;
- private boolean mIgnoreDismiss;
+ private boolean mPendingSharedTransition;
private final ArrayList<ScreenshotActionChip> mSmartChips = new ArrayList<>();
private PendingInteraction mPendingInteraction;
@@ -178,6 +179,8 @@
mDisplayMetrics = new DisplayMetrics();
mContext.getDisplay().getRealMetrics(mDisplayMetrics);
+
+ mAccessibilityManager = AccessibilityManager.getInstance(mContext);
}
/**
@@ -292,6 +295,10 @@
requestFocus();
}
+ View getScreenshotPreview() {
+ return mScreenshotPreview;
+ }
+
/**
* Set up the logger and callback on dismissal.
*
@@ -331,8 +338,10 @@
mScreenshotPreview.setScaleX(currentScale);
mScreenshotPreview.setScaleY(currentScale);
- mDismissButton.setAlpha(0);
- mDismissButton.setVisibility(View.VISIBLE);
+ if (mAccessibilityManager.isEnabled()) {
+ mDismissButton.setAlpha(0);
+ mDismissButton.setVisibility(View.VISIBLE);
+ }
AnimatorSet dropInAnimation = new AnimatorSet();
ValueAnimator flashInAnimator = ValueAnimator.ofFloat(0, 1);
@@ -529,44 +538,22 @@
});
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) {
@@ -605,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");
}
@@ -665,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);
@@ -692,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);
@@ -719,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/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index f758db8..9d52fe5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -19,11 +19,11 @@
import android.app.ActivityManager
import android.content.res.Resources
import android.os.SystemProperties
+import android.util.IndentingPrintWriter
import android.util.MathUtils
import android.view.SurfaceControl
import android.view.ViewRootImpl
import androidx.annotation.VisibleForTesting
-import com.android.internal.util.IndentingPrintWriter
import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
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/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 01aa53f..ead6d32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -21,6 +21,7 @@
import android.animation.ValueAnimator
import android.app.WallpaperManager
import android.os.SystemClock
+import android.util.IndentingPrintWriter
import android.util.Log
import android.util.MathUtils
import android.view.Choreographer
@@ -29,7 +30,6 @@
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
-import com.android.internal.util.IndentingPrintWriter
import com.android.systemui.Dumpable
import com.android.systemui.Interpolators
import com.android.systemui.dagger.SysUISingleton
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 e43130f..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;
@@ -642,9 +649,8 @@
BUBBLE_PREFERENCE_SELECTED);
}
if (mBubblesManagerOptional.isPresent()) {
- post(() -> {
- mBubblesManagerOptional.get().onUserChangedImportance(mEntry);
- });
+ post(() -> mBubblesManagerOptional.get()
+ .onUserSetImportantConversation(mEntry));
}
}
mChannelToUpdate.setImportance(Math.max(
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 194776e..d132abe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -129,7 +129,6 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-import com.android.systemui.util.InjectionInflationController;
import com.android.wm.shell.animation.FlingAnimationUtils;
import java.io.FileDescriptor;
@@ -266,8 +265,7 @@
&& mAuthController.getUdfpsRegion() != null
&& mAuthController.isUdfpsEnrolled(
KeyguardUpdateMonitor.getCurrentUser())) {
- LayoutInflater.from(mView.getContext())
- .inflate(R.layout.disabled_udfps_view, mView);
+ mLayoutInflater.inflate(R.layout.disabled_udfps_view, mView);
mDisabledUdfpsController = new DisabledUdfpsController(
mView.findViewById(R.id.disabled_udfps_view),
mStatusBarStateController,
@@ -279,7 +277,7 @@
}
};
- private final InjectionInflationController mInjectionInflationController;
+ private final LayoutInflater mLayoutInflater;
private final PowerManager mPowerManager;
private final AccessibilityManager mAccessibilityManager;
private final NotificationWakeUpCoordinator mWakeUpCoordinator;
@@ -523,7 +521,7 @@
@Inject
public NotificationPanelViewController(NotificationPanelView view,
@Main Resources resources,
- InjectionInflationController injectionInflationController,
+ LayoutInflater layoutInflater,
NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
DynamicPrivacyController dynamicPrivacyController,
KeyguardBypassController bypassController, FalsingManager falsingManager,
@@ -568,7 +566,7 @@
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
mQSDetailDisplayer = qsDetailDisplayer;
mView.setWillNotDraw(!DEBUG);
- mInjectionInflationController = injectionInflationController;
+ mLayoutInflater = layoutInflater;
mFalsingManager = falsingManager;
mFalsingCollector = falsingCollector;
mPowerManager = powerManager;
@@ -774,8 +772,7 @@
KeyguardStatusView keyguardStatusView = mView.findViewById(R.id.keyguard_status_view);
int index = mView.indexOfChild(keyguardStatusView);
mView.removeView(keyguardStatusView);
- keyguardStatusView = (KeyguardStatusView) mInjectionInflationController.injectable(
- LayoutInflater.from(mView.getContext())).inflate(
+ keyguardStatusView = (KeyguardStatusView) mLayoutInflater.inflate(
R.layout.keyguard_status_view, mView, false);
mView.addView(keyguardStatusView, index);
@@ -786,8 +783,7 @@
index = mView.indexOfChild(mKeyguardBottomArea);
mView.removeView(mKeyguardBottomArea);
KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea;
- mKeyguardBottomArea = (KeyguardBottomAreaView) mInjectionInflationController.injectable(
- LayoutInflater.from(mView.getContext())).inflate(
+ mKeyguardBottomArea = (KeyguardBottomAreaView) mLayoutInflater.inflate(
R.layout.keyguard_bottom_area, mView, false);
mKeyguardBottomArea.initFrom(oldBottomArea);
mView.addView(mKeyguardBottomArea, index);
@@ -2412,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 ab96a6d..fc1811b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -370,13 +370,15 @@
if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) {
mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY;
scheduleUpdate();
- } else if ((!mDozeParameters.getAlwaysOn() && oldState == ScrimState.AOD)
+ } else if ((oldState == ScrimState.AOD // leaving doze
+ && (!mDozeParameters.getAlwaysOn() || mState == ScrimState.UNLOCKED))
|| (mState == ScrimState.AOD && !mDozeParameters.getDisplayNeedsBlanking())) {
// Scheduling a frame isn't enough when:
// • Leaving doze and we need to modify scrim color immediately
// • ColorFade will not kick-in and scrim cannot wait for pre-draw.
onPreDraw();
} else {
+ // Schedule a frame
scheduleUpdate();
}
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 7eefaf2..d11e864 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -39,12 +39,14 @@
public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallback,
SecurityController.SecurityControllerCallback, Tunable {
private static final String TAG = "StatusBarSignalPolicy";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final String mSlotAirplane;
private final String mSlotMobile;
private final String mSlotWifi;
private final String mSlotEthernet;
private final String mSlotVpn;
+ private final String mSlotNoCalling;
private final Context mContext;
private final StatusBarIconController mIconController;
@@ -61,9 +63,11 @@
// Track as little state as possible, and only for padding purposes
private boolean mIsAirplaneMode = false;
+ private boolean mIsWifiEnabled = false;
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) {
@@ -74,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;
@@ -139,24 +144,43 @@
public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
boolean activityIn, boolean activityOut, String description, boolean isTransient,
String statusLabel) {
-
+ if (DEBUG) {
+ Log.d(TAG, "setWifiIndicators: "
+ + "enabled = " + enabled + ","
+ + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + ","
+ + "qsIcon = " + (qsIcon == null ? "" : qsIcon.toString()) + ","
+ + "activityIn = " + activityIn + ","
+ + "activityOut = " + activityOut + ","
+ + "description = " + description + ","
+ + "isTransient = " + isTransient + ","
+ + "statusLabel = " + statusLabel);
+ }
boolean visible = statusIcon.visible && !mHideWifi;
boolean in = activityIn && mActivityEnabled && visible;
boolean out = activityOut && mActivityEnabled && visible;
+ mIsWifiEnabled = enabled;
WifiIconState newState = mWifiIconState.copy();
- newState.visible = visible;
- newState.resId = statusIcon.icon;
- newState.activityIn = in;
- newState.activityOut = out;
+ if (mWifiIconState.noDefaultNetwork && mWifiIconState.noNetworksAvailable
+ && !mIsAirplaneMode) {
+ newState.visible = true;
+ newState.resId = R.drawable.ic_qs_no_internet_unavailable;
+ } else if (mWifiIconState.noValidatedNetwork && !mWifiIconState.noNetworksAvailable
+ && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) {
+ newState.visible = true;
+ newState.resId = R.drawable.ic_qs_no_internet_available;
+ } else {
+ newState.visible = visible;
+ newState.resId = statusIcon.icon;
+ newState.activityIn = in;
+ newState.activityOut = out;
+ newState.contentDescription = statusIcon.contentDescription;
+ MobileIconState first = getFirstMobileState();
+ newState.signalSpacerVisible = first != null && first.typeId != 0;
+ }
newState.slot = mSlotWifi;
newState.airplaneSpacerVisible = mIsAirplaneMode;
- newState.contentDescription = statusIcon.contentDescription;
-
- MobileIconState first = getFirstMobileState();
- newState.signalSpacerVisible = first != null && first.typeId != 0;
-
updateWifiIconWithState(newState);
mWifiIconState = newState;
}
@@ -167,6 +191,7 @@
}
private void updateWifiIconWithState(WifiIconState state) {
+ if (DEBUG) Log.d(TAG, "WifiIconState: " + state == null ? "" : state.toString());
if (state.visible && state.resId > 0) {
mIconController.setSignalIcon(mSlotWifi, state);
mIconController.setIconVisibility(mSlotWifi, true);
@@ -176,11 +201,42 @@
}
@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,
CharSequence typeContentDescriptionHtml, CharSequence description,
boolean isWide, int subId, boolean roaming) {
+ if (DEBUG) {
+ Log.d(TAG, "setMobileDataIndicators: "
+ + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + ","
+ + "qsIcon = " + (qsIcon == null ? "" : qsIcon.toString()) + ","
+ + "statusType = " + statusType + ","
+ + "qsType = " + qsType + ","
+ + "activityIn = " + activityIn + ","
+ + "activityOut = " + activityOut + ","
+ + "typeContentDescription = " + typeContentDescription + ","
+ + "typeContentDescriptionHtml = " + typeContentDescriptionHtml + ","
+ + "description = " + description + ","
+ + "isWide = " + isWide + ","
+ + "subId = " + subId + ","
+ + "roaming = " + roaming);
+ }
MobileIconState state = getState(subId);
if (state == null) {
return;
@@ -198,6 +254,10 @@
state.activityIn = activityIn && mActivityEnabled;
state.activityOut = activityOut && mActivityEnabled;
+ if (DEBUG) {
+ Log.d(TAG, "MobileIconStates: "
+ + (mMobileStates == null ? "" : mMobileStates.toString()));
+ }
// Always send a copy to maintain value type semantics
mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates));
@@ -211,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) {
@@ -237,15 +307,18 @@
*/
@Override
public void setSubs(List<SubscriptionInfo> subs) {
+ if (DEBUG) Log.d(TAG, "setSubs: " + (subs == null ? "" : subs.toString()));
if (hasCorrectSubs(subs)) {
return;
}
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()));
}
}
@@ -267,6 +340,36 @@
// Noop yay!
}
+ @Override
+ public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork,
+ boolean noNetworksAvailable) {
+ if (DEBUG) {
+ Log.d(TAG, "setConnectivityStatus: "
+ + "noDefaultNetwork = " + noDefaultNetwork + ","
+ + "noValidatedNetwork = " + noValidatedNetwork + ","
+ + "noNetworksAvailable = " + noNetworksAvailable);
+ }
+ WifiIconState newState = mWifiIconState.copy();
+ newState.noDefaultNetwork = noDefaultNetwork;
+ newState.noValidatedNetwork = noValidatedNetwork;
+ newState.noNetworksAvailable = noNetworksAvailable;
+ newState.slot = mSlotWifi;
+ newState.airplaneSpacerVisible = mIsAirplaneMode;
+ if (noDefaultNetwork && noNetworksAvailable && !mIsAirplaneMode) {
+ newState.visible = true;
+ newState.resId = R.drawable.ic_qs_no_internet_unavailable;
+ } else if (noValidatedNetwork && !noNetworksAvailable
+ && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) {
+ newState.visible = true;
+ newState.resId = R.drawable.ic_qs_no_internet_available;
+ } else {
+ newState.visible = false;
+ newState.resId = 0;
+ }
+ updateWifiIconWithState(newState);
+ mWifiIconState = newState;
+ }
+
@Override
public void setEthernetIndicators(IconState state) {
@@ -284,6 +387,10 @@
@Override
public void setIsAirplaneMode(IconState icon) {
+ if (DEBUG) {
+ Log.d(TAG, "setIsAirplaneMode: "
+ + "icon = " + (icon == null ? "" : icon.toString()));
+ }
mIsAirplaneMode = icon.visible && !mHideAirplane;
int resId = icon.icon;
String description = icon.contentDescription;
@@ -301,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;
@@ -340,6 +494,9 @@
public int resId;
public boolean airplaneSpacerVisible;
public boolean signalSpacerVisible;
+ public boolean noDefaultNetwork;
+ public boolean noValidatedNetwork;
+ public boolean noNetworksAvailable;
@Override
public boolean equals(Object o) {
@@ -351,9 +508,12 @@
return false;
}
WifiIconState that = (WifiIconState) o;
- return resId == that.resId &&
- airplaneSpacerVisible == that.airplaneSpacerVisible &&
- signalSpacerVisible == that.signalSpacerVisible;
+ return resId == that.resId
+ && airplaneSpacerVisible == that.airplaneSpacerVisible
+ && signalSpacerVisible == that.signalSpacerVisible
+ && noDefaultNetwork == that.noDefaultNetwork
+ && noValidatedNetwork == that.noValidatedNetwork
+ && noNetworksAvailable == that.noNetworksAvailable;
}
public void copyTo(WifiIconState other) {
@@ -361,6 +521,9 @@
other.resId = resId;
other.airplaneSpacerVisible = airplaneSpacerVisible;
other.signalSpacerVisible = signalSpacerVisible;
+ other.noDefaultNetwork = noDefaultNetwork;
+ other.noValidatedNetwork = noValidatedNetwork;
+ other.noNetworksAvailable = noNetworksAvailable;
}
public WifiIconState copy() {
@@ -372,7 +535,8 @@
@Override
public int hashCode() {
return Objects.hash(super.hashCode(),
- resId, airplaneSpacerVisible, signalSpacerVisible);
+ resId, airplaneSpacerVisible, signalSpacerVisible, noDefaultNetwork,
+ noValidatedNetwork, noNetworksAvailable);
}
@Override public String toString() {
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 97d348b..5e88cd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -135,6 +135,26 @@
}
@Override
+ public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork,
+ boolean noNetworksAvailable) {
+ post(() -> {
+ for (SignalCallback signalCluster : mSignalCallbacks) {
+ signalCluster.setConnectivityStatus(
+ noDefaultNetwork, noValidatedNetwork, noNetworksAvailable);
+ }
+ });
+ }
+
+ @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 947b9182..2f66508 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -36,6 +36,7 @@
import android.telephony.TelephonyManager;
import android.text.Html;
import android.text.TextUtils;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -66,6 +67,7 @@
private final String mNetworkNameDefault;
private final String mNetworkNameSeparator;
private final ContentObserver mObserver;
+ private final boolean mProviderModel;
// Save entire info for logging, we only use the id.
final SubscriptionInfo mSubscriptionInfo;
// @VisibleForDemoMode
@@ -140,6 +142,8 @@
};
mMobileStatusTracker = new MobileStatusTracker(mPhone, receiverLooper,
info, mDefaults, mCallback);
+ mProviderModel = FeatureFlagUtils.isEnabled(
+ mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
}
public void setConfiguration(Config config) {
@@ -236,6 +240,11 @@
@Override
public void notifyListeners(SignalCallback callback) {
+ // If the device is on carrier merged WiFi, we should let WifiSignalController to control
+ // the SysUI states.
+ if (mNetworkController.isCarrierMergedWifi(mSubscriptionInfo.getSubscriptionId())) {
+ return;
+ }
MobileIconGroup icons = getIcons();
String contentDescription = getTextIfExists(getContentDescription()).toString();
@@ -253,33 +262,65 @@
|| (mCurrentState.iconGroup == TelephonyIcons.NOT_DEFAULT_DATA))
&& mCurrentState.userSetup;
- // Show icon in QS when we are connected or data is disabled.
- boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
- IconState statusIcon = new IconState(mCurrentState.enabled && !mCurrentState.airplaneMode,
- getCurrentIconId(), contentDescription);
+ if (mProviderModel) {
+ // Show icon in QS when we are connected or data is disabled.
+ boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
- int qsTypeIcon = 0;
- IconState qsIcon = null;
- CharSequence description = null;
- // Only send data sim callbacks to QS.
- if (mCurrentState.dataSim) {
- qsTypeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.qsDataType : 0;
- qsIcon = new IconState(mCurrentState.enabled
- && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription);
- description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
+ int qsTypeIcon = 0;
+ IconState qsIcon = null;
+ CharSequence description = null;
+ // Only send data sim callbacks to QS.
+ if (mCurrentState.dataSim && mCurrentState.isDefault) {
+ qsTypeIcon =
+ (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.qsDataType : 0;
+ qsIcon = new IconState(mCurrentState.enabled
+ && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription);
+ description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
+ }
+ boolean activityIn = mCurrentState.dataConnected
+ && !mCurrentState.carrierNetworkChangeMode
+ && mCurrentState.activityIn;
+ boolean activityOut = mCurrentState.dataConnected
+ && !mCurrentState.carrierNetworkChangeMode
+ && mCurrentState.activityOut;
+ showDataIcon &= mCurrentState.dataSim && mCurrentState.isDefault;
+ IconState statusIcon = new IconState(showDataIcon && !mCurrentState.airplaneMode,
+ getCurrentIconId(), contentDescription);
+ int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
+ callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
+ activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
+ description, icons.isWide, mSubscriptionInfo.getSubscriptionId(),
+ mCurrentState.roaming);
+ } else {
+ boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
+ IconState statusIcon = new IconState(
+ mCurrentState.enabled && !mCurrentState.airplaneMode,
+ getCurrentIconId(), contentDescription);
+
+ int qsTypeIcon = 0;
+ IconState qsIcon = null;
+ CharSequence description = null;
+ // Only send data sim callbacks to QS.
+ if (mCurrentState.dataSim) {
+ qsTypeIcon =
+ (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.qsDataType : 0;
+ qsIcon = new IconState(mCurrentState.enabled
+ && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription);
+ description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
+ }
+ boolean activityIn = mCurrentState.dataConnected
+ && !mCurrentState.carrierNetworkChangeMode
+ && mCurrentState.activityIn;
+ boolean activityOut = mCurrentState.dataConnected
+ && !mCurrentState.carrierNetworkChangeMode
+ && mCurrentState.activityOut;
+ showDataIcon &= mCurrentState.isDefault || dataDisabled;
+ int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
+ callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
+ activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
+ description, icons.isWide, mSubscriptionInfo.getSubscriptionId(),
+ mCurrentState.roaming);
}
- boolean activityIn = mCurrentState.dataConnected
- && !mCurrentState.carrierNetworkChangeMode
- && mCurrentState.activityIn;
- boolean activityOut = mCurrentState.dataConnected
- && !mCurrentState.carrierNetworkChangeMode
- && mCurrentState.activityOut;
- showDataIcon &= mCurrentState.isDefault || dataDisabled;
- int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
- callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
- activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
- description, icons.isWide, mSubscriptionInfo.getSubscriptionId(),
- mCurrentState.roaming);
}
@Override
@@ -299,6 +340,18 @@
return Utils.isInService(mServiceState);
}
+ String getNonDefaultCarrierName() {
+ if (!mCurrentState.networkNameData.equals(mNetworkNameDefault)) {
+ return mCurrentState.networkNameData;
+ } else if (mSubscriptionInfo.getCarrierName() != null) {
+ return mSubscriptionInfo.getCarrierName().toString();
+ } else if (mSubscriptionInfo.getDisplayName() != null) {
+ return mSubscriptionInfo.getDisplayName().toString();
+ } else {
+ return "";
+ }
+ }
+
private boolean isRoaming() {
// During a carrier change, roaming indications need to be supressed.
if (isCarrierNetworkChangeActive()) {
@@ -405,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 b012dc4..f2b0d76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -83,6 +83,22 @@
default void setIsAirplaneMode(IconState icon) {}
default void setMobileDataEnabled(boolean enabled) {}
+
+ /**
+ * Callback for listeners to be able to update the connectivity status
+ * @param noDefaultNetwork whether there is any default network.
+ * @param noValidatedNetwork whether there is any validated network.
+ * @param noNetworksAvailable whether there is any WiFi networks available.
+ */
+ default void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork,
+ boolean noNetworksAvailable) {}
+
+ /**
+ * Callback for listeners to be able to update the no calling & SMS status
+ * @param noCalling whether the calling and SMS is not working.
+ * @param subId subscription ID for which to update the UI
+ */
+ default void setNoCallingStatus(boolean noCalling, int subId) {}
}
public interface EmergencyListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 5f5a83c..92d013e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -34,6 +34,7 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkScoreManager;
+import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -49,6 +50,7 @@
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.MathUtils;
import android.util.SparseArray;
@@ -110,6 +112,7 @@
private final BroadcastDispatcher mBroadcastDispatcher;
private final DemoModeController mDemoModeController;
private final Object mLock = new Object();
+ private final boolean mProviderModel;
private Config mConfig;
private PhoneStateListener mPhoneStateListener;
@@ -140,6 +143,8 @@
// States that don't belong to a subcontroller.
private boolean mAirplaneMode = false;
private boolean mHasNoSubs;
+ private boolean mNoDefaultNetwork = false;
+ private boolean mNoNetworksAvailable = true;
private Locale mLocale = null;
// This list holds our ordering.
private List<SubscriptionInfo> mCurrentSubscriptions = new ArrayList<>();
@@ -270,11 +275,39 @@
}
});
+ WifiManager.ScanResultsCallback scanResultsCallback =
+ new WifiManager.ScanResultsCallback() {
+ @Override
+ public void onScanResultsAvailable() {
+ mNoNetworksAvailable = true;
+ for (ScanResult scanResult : mWifiManager.getScanResults()) {
+ if (!scanResult.SSID.equals(mWifiSignalController.getState().ssid)) {
+ mNoNetworksAvailable = false;
+ break;
+ }
+ }
+ // Only update the network availability if there is no default network.
+ if (mNoDefaultNetwork) {
+ mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
+ mNoNetworksAvailable);
+ }
+ }
+ };
+
+ mWifiManager.registerScanResultsCallback(mReceiverHandler::post, scanResultsCallback);
+
ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback(){
private Network mLastNetwork;
private NetworkCapabilities mLastNetworkCapabilities;
@Override
+ public void onLost(Network network) {
+ mLastNetwork = null;
+ mLastNetworkCapabilities = null;
+ updateConnectivity();
+ }
+
+ @Override
public void onCapabilitiesChanged(
Network network, NetworkCapabilities networkCapabilities) {
boolean lastValidated = (mLastNetworkCapabilities != null) &&
@@ -322,6 +355,8 @@
};
mDemoModeController.addCallback(this);
+ mProviderModel = FeatureFlagUtils.isEnabled(
+ mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
}
private final Runnable mClearForceValidated = () -> {
@@ -441,14 +476,18 @@
private MobileSignalController getDataController() {
int dataSubId = mSubDefaults.getActiveDataSubId();
- if (!SubscriptionManager.isValidSubscriptionId(dataSubId)) {
+ return getControllerWithSubId(dataSubId);
+ }
+
+ private MobileSignalController getControllerWithSubId(int subId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
if (DEBUG) Log.e(TAG, "No data sim selected");
return mDefaultSignalController;
}
- if (mMobileSignalControllers.indexOfKey(dataSubId) >= 0) {
- return mMobileSignalControllers.get(dataSubId);
+ if (mMobileSignalControllers.indexOfKey(subId) >= 0) {
+ return mMobileSignalControllers.get(subId);
}
- if (DEBUG) Log.e(TAG, "Cannot find controller for data sub: " + dataSubId);
+ if (DEBUG) Log.e(TAG, "Cannot find controller for data sub: " + subId);
return mDefaultSignalController;
}
@@ -478,6 +517,15 @@
return dataController.isDataDisabled();
}
+ boolean isCarrierMergedWifi(int subId) {
+ return mWifiSignalController.isCarrierMergedWifi(subId);
+ }
+
+ String getNonDefaultMobileDataNetworkName(int subId) {
+ MobileSignalController controller = getControllerWithSubId(subId);
+ return controller != null ? controller.getNonDefaultCarrierName() : "";
+ }
+
private void notifyControllersMobileDataChanged() {
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
@@ -539,6 +587,9 @@
cb.setIsAirplaneMode(new IconState(mAirplaneMode,
TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext));
cb.setNoSims(mHasNoSubs, mSimDetected);
+ if (mProviderModel) {
+ cb.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition, mNoNetworksAvailable);
+ }
mWifiSignalController.notifyListeners(cb);
mEthernetSignalController.notifyListeners(cb);
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
@@ -883,6 +934,12 @@
mInetCondition = !mValidatedTransports.isEmpty();
pushConnectivityToSignals();
+ if (mProviderModel) {
+ mNoDefaultNetwork = mConnectedTransports.isEmpty();
+ mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
+ mNoNetworksAvailable);
+ notifyAllListeners();
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 1d77841..4f4a504 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -33,7 +33,6 @@
import android.net.ConnectivityManager.NetworkCallback;
import android.net.IConnectivityManager;
import android.net.Network;
-import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.Handler;
import android.os.RemoteException;
@@ -75,12 +74,8 @@
private static final String TAG = "SecurityController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final NetworkRequest REQUEST = new NetworkRequest.Builder()
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .setUids(null)
- .build();
+ private static final NetworkRequest REQUEST =
+ new NetworkRequest.Builder().clearCapabilities().build();
private static final int NO_NETWORK = -1;
private static final String VPN_BRANDED_META_DATA = "com.android.systemui.IS_BRANDED";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyController.java
index 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/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 4954286..7042e2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -27,6 +27,7 @@
import android.net.wifi.WifiManager;
import android.text.Html;
import android.text.TextUtils;
+import android.util.FeatureFlagUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.SignalIcon.IconGroup;
@@ -48,6 +49,7 @@
private final IconGroup mUnmergedWifiIconGroup = WifiIcons.UNMERGED_WIFI;
private final MobileIconGroup mCarrierMergedWifiIconGroup = TelephonyIcons.CARRIER_MERGED_WIFI;
private final WifiManager mWifiManager;
+ private final boolean mProviderModel;
public WifiSignalController(Context context, boolean hasMobileDataFeature,
CallbackHandler callbackHandler, NetworkControllerImpl networkController,
@@ -65,6 +67,8 @@
new WifiTrafficStateCallback());
}
mCurrentState.iconGroup = mLastState.iconGroup = mUnmergedWifiIconGroup;
+ mProviderModel = FeatureFlagUtils.isEnabled(
+ mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
}
@Override
@@ -100,12 +104,26 @@
contentDescription += ("," + mContext.getString(R.string.data_connection_no_internet));
}
IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription);
- IconState qsIcon = new IconState(mCurrentState.connected,
- mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
- : getQsCurrentIconId(), contentDescription);
- callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon,
- ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut,
- wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel);
+ if (mProviderModel) {
+ IconState qsIcon = null;
+ if (mCurrentState.isDefault) {
+ qsIcon = new IconState(mCurrentState.connected,
+ mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
+ : getQsCurrentIconId(), contentDescription);
+ }
+ callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon,
+ ssidPresent && mCurrentState.activityIn,
+ ssidPresent && mCurrentState.activityOut,
+ wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel);
+ } else {
+ IconState qsIcon = new IconState(mCurrentState.connected,
+ mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
+ : getQsCurrentIconId(), contentDescription);
+ callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon,
+ ssidPresent && mCurrentState.activityIn,
+ ssidPresent && mCurrentState.activityOut,
+ wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel);
+ }
}
private void notifyListenersForCarrierWifi(SignalCallback callback) {
@@ -127,7 +145,8 @@
int typeIcon = mCurrentState.connected ? icons.dataType : 0;
IconState qsIcon = new IconState(
mCurrentState.connected, getQsCurrentIconIdForCarrierWifi(), contentDescription);
- CharSequence description = mNetworkController.getMobileDataNetworkName();
+ CharSequence description =
+ mNetworkController.getNonDefaultMobileDataNetworkName(mCurrentState.subId);
callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
mCurrentState.activityIn, mCurrentState.activityOut, dataContentDescription,
dataContentDescriptionHtml, description, icons.isWide,
@@ -191,6 +210,11 @@
: mUnmergedWifiIconGroup;
}
+ boolean isCarrierMergedWifi(int subId) {
+ return mCurrentState.isDefault
+ && mCurrentState.isCarrierMerged && (mCurrentState.subId == subId);
+ }
+
@VisibleForTesting
void setActivity(int wifiActivity) {
mCurrentState.activityIn = wifiActivity == DATA_ACTIVITY_INOUT
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/ConvenienceExtensions.kt b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
index ff53a9f..57b3f53 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
@@ -16,8 +16,8 @@
package com.android.systemui.util
+import android.util.IndentingPrintWriter
import android.view.ViewGroup
-import com.android.internal.util.IndentingPrintWriter
import java.io.PrintWriter
/** [Sequence] that yields all of the direct children of this [ViewGroup] */
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/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 844f12e..bf823b4 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -615,12 +615,16 @@
}
/**
- * When a notification is marked Priority, expand the stack if needed,
- * then (maybe create and) select the given bubble.
+ * When a notification is set as important, make it a bubble and expand the stack if
+ * it can bubble.
*
- * @param entry the notification for the bubble to show
+ * @param entry the important notification.
*/
- public void onUserChangedImportance(NotificationEntry entry) {
+ public void onUserSetImportantConversation(NotificationEntry entry) {
+ if (entry.getBubbleMetadata() == null) {
+ // No bubble metadata, nothing to do.
+ return;
+ }
try {
int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
flags |= Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE;
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 a44fcec..bbc238a 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -19,6 +19,7 @@
import static android.os.Process.THREAD_PRIORITY_DISPLAY;
import android.animation.AnimationHandler;
+import android.app.ActivityTaskManager;
import android.app.IActivityManager;
import android.content.Context;
import android.content.pm.LauncherApps;
@@ -71,6 +72,7 @@
import com.android.wm.shell.pip.phone.PipAppOpsListener;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -310,8 +312,18 @@
@BindsOptionalOf
abstract LegacySplitScreen optionalLegacySplitScreen();
- @BindsOptionalOf
- abstract SplitScreen optionalSplitScreen();
+ @WMSingleton
+ @Provides
+ static Optional<SplitScreen> provideSplitScreen(ShellTaskOrganizer shellTaskOrganizer,
+ SyncTransactionQueue syncQueue, Context context,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
+ return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context,
+ rootTaskDisplayAreaOrganizer));
+ } else {
+ return Optional.empty();
+ }
+ }
@BindsOptionalOf
abstract AppPairs optionalAppPairs();
@@ -338,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/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index ee76169..8105250 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -17,6 +17,7 @@
package com.android.systemui.wmshell;
import android.animation.AnimationHandler;
+import android.app.ActivityTaskManager;
import android.content.Context;
import android.view.IWindowManager;
@@ -89,15 +90,6 @@
@WMSingleton
@Provides
- static SplitScreen provideSplitScreen(ShellTaskOrganizer shellTaskOrganizer,
- SyncTransactionQueue syncQueue, Context context,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
- return new SplitScreenController(shellTaskOrganizer, syncQueue, context,
- rootTaskDisplayAreaOrganizer);
- }
-
- @WMSingleton
- @Provides
static AppPairs provideAppPairs(ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue, DisplayController displayController,
@ShellMainThread ShellExecutor mainExecutor) {
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/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index d452861..aa7143e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -40,6 +40,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.DisplayMetrics;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -84,7 +85,6 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.InjectionInflationController;
import com.android.wm.shell.animation.FlingAnimationUtils;
import org.junit.Before;
@@ -139,7 +139,7 @@
@Mock
private NotificationPanelView mView;
@Mock
- private InjectionInflationController mInjectionInflationController;
+ private LayoutInflater mLayoutInflater;
@Mock
private DynamicPrivacyController mDynamicPrivacyController;
@Mock
@@ -264,7 +264,7 @@
.thenReturn(mKeyguardStatusViewController);
mNotificationPanelViewController = new NotificationPanelViewController(mView,
mResources,
- mInjectionInflationController,
+ mLayoutInflater,
coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
new FalsingManagerFake(), new FalsingCollectorFake(), mShadeController,
mNotificationLockscreenUserManager, mNotificationEntryManager,
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/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 8f5d308..273a77b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -25,7 +25,6 @@
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.atLeastOnce;
@@ -58,6 +57,7 @@
import android.telephony.TelephonyManager;
import android.testing.TestableLooper;
import android.testing.TestableResources;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -75,6 +75,7 @@
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TestWatcher;
@@ -121,7 +122,8 @@
protected int mSubId;
private NetworkCapabilities mNetCapabilities;
- private ConnectivityManager.NetworkCallback mDefaultNetworkCallback;
+ private ConnectivityManager.NetworkCallback mDefaultCallbackInWifiTracker;
+ private ConnectivityManager.NetworkCallback mDefaultCallbackInNetworkController;
private ConnectivityManager.NetworkCallback mNetworkCallback;
@Rule
@@ -143,6 +145,7 @@
@Before
public void setUp() throws Exception {
+ FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL, true);
mInstrumentation = InstrumentationRegistry.getInstrumentation();
Settings.Global.putInt(mContext.getContentResolver(), Global.AIRPLANE_MODE_ON, 0);
TestableResources res = mContext.getOrCreateTestableResources();
@@ -222,6 +225,11 @@
mNetworkController.addEmergencyListener(null);
}
+ @After
+ public void tearDown() throws Exception {
+ FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL, false);
+ }
+
protected void setupNetworkController() {
// For now just pretend to be the data sim, so we can test that too.
mSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
@@ -235,8 +243,10 @@
ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
verify(mMockCm, atLeastOnce())
.registerDefaultNetworkCallback(callbackArg.capture(), isA(Handler.class));
- mDefaultNetworkCallback = callbackArg.getValue();
- assertNotNull(mDefaultNetworkCallback);
+ mDefaultCallbackInWifiTracker = callbackArg.getAllValues().get(0);
+ mDefaultCallbackInNetworkController = callbackArg.getAllValues().get(1);
+ assertNotNull(mDefaultCallbackInWifiTracker);
+ assertNotNull(mDefaultCallbackInNetworkController);
verify(mMockCm, atLeastOnce()).registerNetworkCallback(
isA(NetworkRequest.class), callbackArg.capture(), isA(Handler.class));
mNetworkCallback = callbackArg.getValue();
@@ -294,12 +304,22 @@
mNetworkController.onReceive(mContext, i);
}
- public void setConnectivityViaCallback(
+ public void setConnectivityViaCallbackInNetworkController(
int networkType, boolean validated, boolean isConnected, WifiInfo wifiInfo) {
- mNetCapabilities.setTransportInfo(wifiInfo);
+ if (networkType == NetworkCapabilities.TRANSPORT_WIFI) {
+ mNetCapabilities.setTransportInfo(wifiInfo);
+ }
setConnectivityCommon(networkType, validated, isConnected);
- mDefaultNetworkCallback.onCapabilitiesChanged(
- mock(Network.class), new NetworkCapabilities(mNetCapabilities));
+ mDefaultCallbackInNetworkController.onCapabilitiesChanged(
+ mock(Network.class), new NetworkCapabilities(mNetCapabilities));
+ }
+
+ public void setConnectivityViaCallbackInWifiTracker(
+ int networkType, boolean validated, boolean isConnected, WifiInfo wifiInfo) {
+ if (networkType == NetworkCapabilities.TRANSPORT_WIFI) {
+ mNetCapabilities.setTransportInfo(wifiInfo);
+ }
+ setConnectivityCommon(networkType, validated, isConnected);
if (networkType == NetworkCapabilities.TRANSPORT_WIFI) {
if (isConnected) {
mNetworkCallback.onCapabilitiesChanged(
@@ -310,6 +330,16 @@
}
}
+ public void setConnectivityViaDefaultCallbackInWifiTracker(
+ int networkType, boolean validated, boolean isConnected, WifiInfo wifiInfo) {
+ if (networkType == NetworkCapabilities.TRANSPORT_WIFI) {
+ mNetCapabilities.setTransportInfo(wifiInfo);
+ }
+ setConnectivityCommon(networkType, validated, isConnected);
+ mDefaultCallbackInWifiTracker.onCapabilitiesChanged(
+ mock(Network.class), new NetworkCapabilities(mNetCapabilities));
+ }
+
private void setConnectivityCommon(
int networkType, boolean validated, boolean isConnected){
// TODO: Separate out into several NetworkCapabilities.
@@ -457,10 +487,9 @@
any(),
typeIconArg.capture(),
anyInt(), anyBoolean(), anyBoolean(),
- any(CharSequence.class), any(CharSequence.class), any(CharSequence.class),
+ any(CharSequence.class), any(CharSequence.class), any(),
anyBoolean(), anyInt(), eq(roaming));
IconState iconState = iconArg.getValue();
-
int state = icon == -1 ? 0
: SignalDrawable.getState(icon, CellSignalStrength.getNumSignalStrengthLevels(),
!inet);
@@ -507,7 +536,7 @@
dataOutArg.capture(),
typeContentDescriptionArg.capture(),
typeContentDescriptionHtmlArg.capture(),
- anyString(), anyBoolean(), anyInt(), anyBoolean());
+ any(), anyBoolean(), anyInt(), anyBoolean());
IconState iconState = iconArg.getValue();
@@ -522,8 +551,12 @@
assertEquals("Visibility in status bar", visible, iconState.visible);
iconState = qsIconArg.getValue();
- assertEquals("Visibility in quick settings", qsVisible, iconState.visible);
- assertEquals("Signal icon in quick settings", state, iconState.icon);
+ if (visible) {
+ assertEquals("Visibility in quick settings", qsVisible, iconState.visible);
+ assertEquals("Signal icon in quick settings", state, iconState.icon);
+ } else {
+ assertEquals("Cellular is not default", null, iconState);
+ }
assertEquals("Data icon in quick settings", qsTypeIcon, (int) qsTypeIconArg.getValue());
assertEquals("Data direction in in quick settings", dataIn,
(boolean) dataInArg.getValue());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index d11aee8..37b6a5d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -129,7 +129,7 @@
setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
// Verify that a SignalDrawable with a cut out is used to display data disabled.
- verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, 0,
+ verifyLastMobileDataIndicators(false, DEFAULT_SIGNAL_STRENGTH, 0,
true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false,
false, true, NO_DATA_STRING, NO_DATA_STRING);
}
@@ -143,7 +143,7 @@
setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
// Verify that a SignalDrawable with a cut out is used to display data disabled.
- verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, 0,
+ verifyLastMobileDataIndicators(false, DEFAULT_SIGNAL_STRENGTH, 0,
true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false,
false, true, NO_DATA_STRING, NO_DATA_STRING);
}
@@ -158,7 +158,7 @@
setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
// Verify that a SignalDrawable with a cut out is used to display data disabled.
- verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, 0,
+ verifyLastMobileDataIndicators(false, DEFAULT_SIGNAL_STRENGTH, 0,
true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false,
false, false, NOT_DEFAULT_DATA_STRING, NOT_DEFAULT_DATA_STRING);
}
@@ -173,7 +173,7 @@
setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
// Verify that a SignalDrawable with a cut out is used to display data disabled.
- verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, 0,
+ verifyLastMobileDataIndicators(false, DEFAULT_SIGNAL_STRENGTH, 0,
true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false,
false, false, NOT_DEFAULT_DATA_STRING, NOT_DEFAULT_DATA_STRING);
}
@@ -190,7 +190,8 @@
TestableLooper.get(this).processAllMessages();
// Don't show the X until the device is setup.
- verifyDataIndicators(0);
+ verifyLastMobileDataIndicators(false, DEFAULT_SIGNAL_STRENGTH, 0,
+ true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false, false);
}
@Test
@@ -205,7 +206,9 @@
mConfig.alwaysShowDataRatIcon = true;
mNetworkController.handleConfigurationChanged();
- verifyDataIndicators(TelephonyIcons.ICON_G);
+ setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
+ verifyLastMobileDataIndicators(false, DEFAULT_SIGNAL_STRENGTH, TelephonyIcons.ICON_G,
+ true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false, false);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index da35de9..c0d9c3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -262,7 +262,7 @@
setConnectivityViaBroadcast(mMobileSignalController.mTransportType, false, false);
setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
- verifyLastMobileDataIndicators(true, DEFAULT_LEVEL, 0);
+ verifyLastMobileDataIndicators(false, DEFAULT_LEVEL, 0);
}
// Some tests of actual NetworkController code, just internals not display stuff
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 477756e..44c5edb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -49,16 +49,16 @@
setWifiState(true, testSsid);
setWifiLevel(0);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
+
// Connected, but still not validated - does not show
verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]);
for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
setWifiLevel(testLevel);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
+ setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
+ setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, false, true);
// Icon does not show if not validated
verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
}
@@ -77,11 +77,12 @@
setWifiState(true, testSsid);
for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
setWifiLevel(testLevel);
-
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
+ setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+ setConnectivityViaDefaultCallbackInWifiTracker(
+ NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][testLevel],
testSsid);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
+ setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, false, true);
verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[0][testLevel],
testSsid);
}
@@ -95,7 +96,9 @@
setWifiEnabled(true);
setWifiState(true, testSsid);
setWifiLevel(testLevel);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
+ setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+ setConnectivityViaDefaultCallbackInWifiTracker(
+ NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
verifyLastQsWifiIcon(true, true,
WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][testLevel], testSsid);
@@ -120,17 +123,15 @@
setWifiEnabled(true);
setWifiState(true, testSsid);
setWifiLevel(testLevel);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
+ setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
setupDefaultSignal();
setGsmRoaming(true);
// Still be on wifi though.
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_CELLULAR, false, false, mWifiInfo);
- verifyLastMobileDataIndicators(true,
- DEFAULT_LEVEL,
- 0, true);
+ setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+ setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
+ verifyLastMobileDataIndicators(false, DEFAULT_LEVEL, 0, true);
}
@Test
@@ -141,10 +142,11 @@
setWifiEnabled(true);
setWifiState(true, testSsid);
setWifiLevel(testLevel);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
+ setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
}
@@ -156,11 +158,12 @@
setWifiEnabled(true);
setWifiState(true, testSsid);
setWifiLevel(testLevel);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
+ setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
setWifiState(false, testSsid);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, false, mWifiInfo);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_WIFI, false, false, mWifiInfo);
verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
}
@@ -171,14 +174,17 @@
setWifiEnabled(true);
verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_VPN, false, true, mWifiInfo);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_VPN, true, true, mWifiInfo);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_VPN, false, true, mWifiInfo);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_VPN, true, true, mWifiInfo);
verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
// Mock calling setUnderlyingNetworks.
setWifiState(true, testSsid);
setWifiLevel(testLevel);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
}
@@ -219,9 +225,8 @@
// Put RSSI in the middle of the range.
rssi += amountPerLevel / 2;
when(mWifiInfo.getRssi()).thenReturn(rssi);
- Intent i = new Intent(WifiManager.RSSI_CHANGED_ACTION);
- i.putExtra(WifiManager.EXTRA_NEW_RSSI, rssi);
- mNetworkController.onReceive(mContext, i);
+ setConnectivityViaCallbackInWifiTracker(
+ NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
}
protected void setWifiEnabled(boolean enabled) {
@@ -231,14 +236,9 @@
}
protected void setWifiState(boolean connected, String ssid) {
- Intent i = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- NetworkInfo networkInfo = mock(NetworkInfo.class);
- when(networkInfo.isConnected()).thenReturn(connected);
when(mWifiInfo.getSSID()).thenReturn(ssid);
- when(mMockWm.getConnectionInfo()).thenReturn(mWifiInfo);
-
- i.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
- mNetworkController.onReceive(mContext, i);
+ setConnectivityViaCallbackInWifiTracker(
+ NetworkCapabilities.TRANSPORT_WIFI, false, connected, mWifiInfo);
}
protected void verifyLastQsDataDirection(boolean in, boolean out) {
@@ -263,9 +263,13 @@
anyBoolean(), descArg.capture(), anyBoolean(), any());
IconState iconState = iconArg.getValue();
assertEquals("WiFi enabled, in quick settings", enabled, (boolean) enabledArg.getValue());
- assertEquals("WiFi connected, in quick settings", connected, iconState.visible);
- assertEquals("WiFi signal, in quick settings", icon, iconState.icon);
assertEquals("WiFI desc (ssid), in quick settings", description, descArg.getValue());
+ if (enabled && connected) {
+ assertEquals("WiFi connected, in quick settings", connected, iconState.visible);
+ assertEquals("WiFi signal, in quick settings", icon, iconState.icon);
+ } else {
+ assertEquals("WiFi is not default", null, iconState);
+ }
}
protected void verifyLastWifiIcon(boolean visible, int icon) {
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 25f7fb6..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);
}
}
}
@@ -15320,10 +15327,10 @@
*/
@GuardedBy("this")
void tempWhitelistForPendingIntentLocked(int callerPid, int callerUid, int targetUid,
- long duration, String tag) {
+ long duration, int type, String tag) {
if (DEBUG_WHITELISTS) {
Slog.d(TAG, "tempWhitelistForPendingIntentLocked(" + callerPid + ", " + callerUid + ", "
- + targetUid + ", " + duration + ")");
+ + targetUid + ", " + duration + ", " + type + ")");
}
synchronized (mPidsSelfLocked) {
@@ -15335,7 +15342,11 @@
}
if (!pr.whitelistManager) {
if (checkPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST, callerPid, callerUid)
- != PackageManager.PERMISSION_GRANTED) {
+ != PackageManager.PERMISSION_GRANTED
+ && checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callerPid, callerUid)
+ != PackageManager.PERMISSION_GRANTED
+ && checkPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND, callerPid,
+ callerUid) != PackageManager.PERMISSION_GRANTED) {
if (DEBUG_WHITELISTS) {
Slog.d(TAG, "tempWhitelistForPendingIntentLocked() for target " + targetUid
+ ": pid " + callerPid + " is not allowed");
@@ -15345,8 +15356,7 @@
}
}
- tempWhitelistUidLocked(targetUid, duration, tag,
- BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED);
+ tempWhitelistUidLocked(targetUid, duration, tag, type);
}
/**
@@ -15354,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);
}
}
@@ -15384,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);
}
}
@@ -15401,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")
@@ -15710,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();
}
@@ -15949,9 +15970,9 @@
@Override
public void setPendingIntentWhitelistDuration(IIntentSender target, IBinder whitelistToken,
- long duration) {
+ long duration, int type) {
mPendingIntentController.setPendingIntentWhitelistDuration(target, whitelistToken,
- duration);
+ duration, type);
}
@Override
@@ -15994,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);
}
}
@@ -16367,10 +16394,10 @@
@Override
public void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid,
- long duration, String tag) {
+ long duration, int type, String tag) {
synchronized (ActivityManagerService.this) {
ActivityManagerService.this.tempWhitelistForPendingIntentLocked(
- callerPid, callerUid, targetUid, duration, tag);
+ callerPid, callerUid, targetUid, duration, type, tag);
}
}
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/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index 2ae3d35..42172bf 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -301,13 +301,14 @@
}
void setPendingIntentWhitelistDuration(IIntentSender target, IBinder whitelistToken,
- long duration) {
+ long duration, int type) {
if (!(target instanceof PendingIntentRecord)) {
Slog.w(TAG, "markAsSentFromNotification(): not a PendingIntentRecord: " + target);
return;
}
synchronized (mLock) {
- ((PendingIntentRecord) target).setWhitelistDurationLocked(whitelistToken, duration);
+ ((PendingIntentRecord) target).setWhitelistDurationLocked(whitelistToken, duration,
+ type);
}
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index fbfed34..631b632 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -24,6 +24,7 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
+import android.app.BroadcastOptions;
import android.app.PendingIntent;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
@@ -37,6 +38,7 @@
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Pair;
import android.util.Slog;
import android.util.TimeUtils;
@@ -61,7 +63,11 @@
public final WeakReference<PendingIntentRecord> ref;
boolean sent = false;
boolean canceled = false;
- private ArrayMap<IBinder, Long> whitelistDuration;
+ /**
+ * Map IBinder to duration specified as Pair<Long, Integer>, Long is allowlist duration in
+ * milliseconds, Integer is allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+ */
+ private ArrayMap<IBinder, Pair<Long, Integer>> mWhitelistDuration;
private RemoteCallbackList<IResultReceiver> mCancelCallbacks;
private ArraySet<IBinder> mAllowBgActivityStartsForActivitySender = new ArraySet<>();
private ArraySet<IBinder> mAllowBgActivityStartsForBroadcastSender = new ArraySet<>();
@@ -215,16 +221,16 @@
ref = new WeakReference<>(this);
}
- void setWhitelistDurationLocked(IBinder whitelistToken, long duration) {
+ void setWhitelistDurationLocked(IBinder whitelistToken, long duration, int type) {
if (duration > 0) {
- if (whitelistDuration == null) {
- whitelistDuration = new ArrayMap<>();
+ if (mWhitelistDuration == null) {
+ mWhitelistDuration = new ArrayMap<>();
}
- whitelistDuration.put(whitelistToken, duration);
- } else if (whitelistDuration != null) {
- whitelistDuration.remove(whitelistToken);
- if (whitelistDuration.size() <= 0) {
- whitelistDuration = null;
+ mWhitelistDuration.put(whitelistToken, new Pair(duration, type));
+ } else if (mWhitelistDuration != null) {
+ mWhitelistDuration.remove(whitelistToken);
+ if (mWhitelistDuration.size() <= 0) {
+ mWhitelistDuration = null;
}
}
@@ -292,7 +298,7 @@
if (intent != null) intent.setDefusable(true);
if (options != null) options.setDefusable(true);
- Long duration = null;
+ Pair<Long, Integer> duration = null;
Intent finalIntent = null;
Intent[] allIntents = null;
String[] allResolvedTypes = null;
@@ -341,8 +347,8 @@
mergedOptions.setCallerOptions(opts);
}
- if (whitelistDuration != null) {
- duration = whitelistDuration.get(whitelistToken);
+ if (mWhitelistDuration != null) {
+ duration = mWhitelistDuration.get(whitelistToken);
}
if (key.type == ActivityManager.INTENT_SENDER_ACTIVITY
@@ -370,24 +376,19 @@
int res = START_SUCCESS;
try {
if (duration != null) {
- int procState = controller.mAmInternal.getUidProcessState(callingUid);
- if (!ActivityManager.isProcStateBackground(procState)) {
- StringBuilder tag = new StringBuilder(64);
- tag.append("pendingintent:");
- UserHandle.formatUid(tag, callingUid);
- tag.append(":");
- if (finalIntent.getAction() != null) {
- tag.append(finalIntent.getAction());
- } else if (finalIntent.getComponent() != null) {
- finalIntent.getComponent().appendShortString(tag);
- } else if (finalIntent.getData() != null) {
- tag.append(finalIntent.getData().toSafeString());
- }
- controller.mAmInternal.tempWhitelistForPendingIntent(callingPid, callingUid,
- uid, duration, tag.toString());
- } else {
- Slog.w(TAG, "Not doing whitelist " + this + ": caller state=" + procState);
+ StringBuilder tag = new StringBuilder(64);
+ tag.append("pendingintent:");
+ UserHandle.formatUid(tag, callingUid);
+ tag.append(":");
+ if (finalIntent.getAction() != null) {
+ tag.append(finalIntent.getAction());
+ } else if (finalIntent.getComponent() != null) {
+ finalIntent.getComponent().appendShortString(tag);
+ } else if (finalIntent.getData() != null) {
+ tag.append(finalIntent.getData().toSafeString());
}
+ controller.mAmInternal.tempWhitelistForPendingIntent(callingPid, callingUid,
+ uid, duration.first, duration.second, tag.toString());
}
boolean sendFinish = finishedReceiver != null;
@@ -532,16 +533,18 @@
pw.print(prefix); pw.print("sent="); pw.print(sent);
pw.print(" canceled="); pw.println(canceled);
}
- if (whitelistDuration != null) {
+ if (mWhitelistDuration != null) {
pw.print(prefix);
pw.print("whitelistDuration=");
- for (int i = 0; i < whitelistDuration.size(); i++) {
+ for (int i = 0; i < mWhitelistDuration.size(); i++) {
if (i != 0) {
pw.print(", ");
}
- pw.print(Integer.toHexString(System.identityHashCode(whitelistDuration.keyAt(i))));
+ pw.print(Integer.toHexString(System.identityHashCode(mWhitelistDuration.keyAt(i))));
pw.print(":");
- TimeUtils.formatDuration(whitelistDuration.valueAt(i), pw);
+ TimeUtils.formatDuration(mWhitelistDuration.valueAt(i).first, pw);
+ pw.print("/");
+ pw.print(mWhitelistDuration.valueAt(i).second);
}
pw.println();
}
@@ -569,15 +572,18 @@
}
sb.append(' ');
sb.append(key.typeName());
- if (whitelistDuration != null) {
+ if (mWhitelistDuration != null) {
sb.append( " (whitelist: ");
- for (int i = 0; i < whitelistDuration.size(); i++) {
+ for (int i = 0; i < mWhitelistDuration.size(); i++) {
if (i != 0) {
sb.append(",");
}
- sb.append(Integer.toHexString(System.identityHashCode(whitelistDuration.keyAt(i))));
+ sb.append(Integer.toHexString(System.identityHashCode(
+ mWhitelistDuration.keyAt(i))));
sb.append(":");
- TimeUtils.formatDuration(whitelistDuration.valueAt(i), sb);
+ TimeUtils.formatDuration(mWhitelistDuration.valueAt(i).first, sb);
+ sb.append("/");
+ sb.append(mWhitelistDuration.valueAt(i).second);
}
sb.append(")");
}
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/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 08eeda2..17627fa 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -8454,28 +8454,29 @@
}
for (AudioMix mix : policyConfig.getMixes()) {
// If mix is requesting privileged capture
- if (mix.getRule().allowPrivilegedPlaybackCapture()) {
- // then it must have CAPTURE_MEDIA_OUTPUT or CAPTURE_AUDIO_OUTPUT permission
- requireCaptureAudioOrMediaOutputPerm |= true;
-
- // and its format must be low quality enough
- String error = mix.canBeUsedForPrivilegedCapture(mix.getFormat());
- if (error != null) {
- Log.e(TAG, error);
+ if (mix.getRule().allowPrivilegedMediaPlaybackCapture()) {
+ // then its format must be low quality enough
+ String privilegedMediaCaptureError =
+ mix.canBeUsedForPrivilegedMediaCapture(mix.getFormat());
+ if (privilegedMediaCaptureError != null) {
+ Log.e(TAG, privilegedMediaCaptureError);
return false;
}
+ // and it must have CAPTURE_MEDIA_OUTPUT or CAPTURE_AUDIO_OUTPUT permission
+ requireCaptureAudioOrMediaOutputPerm |= true;
- // If mix is trying to excplicitly capture USAGE_VOICE_COMMUNICATION
- if (mix.containsMatchAttributeRuleForUsage(
- AudioAttributes.USAGE_VOICE_COMMUNICATION)) {
- // then it must have CAPTURE_USAGE_VOICE_COMMUNICATION_OUTPUT permission
- // Note that for UID, USERID or EXCLDUE rules, the capture will be silenced
- // in AudioPolicyMix
- if (voiceCommunicationCaptureMixes == null) {
- voiceCommunicationCaptureMixes = new ArrayList<AudioMix>();
- }
- voiceCommunicationCaptureMixes.add(mix);
+ }
+ // If mix is trying to explicitly capture USAGE_VOICE_COMMUNICATION
+ if (mix.containsMatchAttributeRuleForUsage(
+ AudioAttributes.USAGE_VOICE_COMMUNICATION)
+ && (mix.getRouteFlags() == mix.ROUTE_FLAG_LOOP_BACK_RENDER)) {
+ // It must have CAPTURE_USAGE_VOICE_COMMUNICATION_OUTPUT permission
+ // Note that for UID, USERID or EXCLDUE rules, the capture will be silenced
+ // in AudioPolicyMix
+ if (voiceCommunicationCaptureMixes == null) {
+ voiceCommunicationCaptureMixes = new ArrayList<AudioMix>();
}
+ voiceCommunicationCaptureMixes.add(mix);
}
// If mix is RENDER|LOOPBACK, then an audio MediaProjection is enough
@@ -8498,7 +8499,7 @@
if (voiceCommunicationCaptureMixes != null && voiceCommunicationCaptureMixes.size() > 0) {
if (!callerHasPermission(
android.Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT)) {
- Log.e(TAG, "Privileged audio capture for voice communication requires "
+ Log.e(TAG, "Audio capture for voice communication requires "
+ "CAPTURE_VOICE_COMMUNICATION_OUTPUT system permission");
return false;
}
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 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 6741942..55ac248 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -52,7 +52,6 @@
import android.hardware.biometrics.SensorPropertiesInternal;
import android.os.Binder;
import android.os.Build;
-import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -62,7 +61,7 @@
import com.android.internal.R;
import com.android.internal.widget.LockPatternUtils;
-import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import java.util.List;
@@ -409,7 +408,7 @@
return hasPermission && keyguardPackage != null && keyguardPackage.equals(clientPackage);
}
- public static String getClientName(@Nullable ClientMonitor<?> 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 9898d76..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 ClientMonitor} 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 ClientMonitor<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/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
similarity index 82%
rename from services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
rename to services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index bbd6523..f3c37ef 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -31,7 +31,8 @@
* 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 ClientMonitor<T> extends LoggableMonitor implements IBinder.DeathRecipient {
+public abstract class BaseClientMonitor extends LoggableMonitor
+ implements IBinder.DeathRecipient {
private static final String TAG = "Biometrics/ClientMonitor";
protected static final boolean DEBUG = true;
@@ -49,7 +50,7 @@
*
* @param clientMonitor Reference of the ClientMonitor that is starting.
*/
- default void onClientStarted(@NonNull ClientMonitor<?> clientMonitor) {}
+ default void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {}
/**
* Invoked when the ClientMonitor operation is complete. This abstracts away asynchronous
@@ -60,22 +61,11 @@
* @param clientMonitor Reference of the ClientMonitor that finished.
* @param success True if the operation completed successfully.
*/
- default void onClientFinished(@NonNull ClientMonitor<?> 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
@@ -91,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
@@ -102,14 +91,13 @@
* @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants
* @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants
*/
- public ClientMonitor(@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;
@@ -131,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) {
@@ -147,10 +127,6 @@
mCallback.onClientStarted(this);
}
- /**
- * Starts the HAL operation specific to the ClientMonitor subclass.
- */
- protected abstract void startHalOperation();
public boolean isAlreadyDone() {
return mAlreadyDone;
@@ -219,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 b430521..aa7faf5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -44,8 +44,8 @@
import java.util.Locale;
/**
- * A scheduler for biometric HAL operations. Maintains a queue of {@link ClientMonitor} operations,
- * without caring about its implementation details. Operations may perform one or more
+ * A scheduler for biometric HAL operations. Maintains a queue of {@link BaseClientMonitor}
+ * operations, without caring about its implementation details. Operations may perform one or more
* interactions with the HAL before finishing.
*/
public class BiometricScheduler {
@@ -88,7 +88,7 @@
static final int STATE_WAITING_FOR_COOKIE = 4;
/**
- * The {@link ClientMonitor.Callback} has been invoked and the client is finished.
+ * The {@link BaseClientMonitor.Callback} has been invoked and the client is finished.
*/
static final int STATE_FINISHED = 5;
@@ -101,20 +101,37 @@
@Retention(RetentionPolicy.SOURCE)
@interface OperationState {}
- @NonNull final ClientMonitor<?> clientMonitor;
- @Nullable final ClientMonitor.Callback mClientCallback;
- @OperationState int state;
+ @NonNull final BaseClientMonitor mClientMonitor;
+ @Nullable final BaseClientMonitor.Callback mClientCallback;
+ @OperationState int mState;
- Operation(@NonNull ClientMonitor<?> clientMonitor,
- @Nullable ClientMonitor.Callback callback) {
- this.clientMonitor = clientMonitor;
+ Operation(@NonNull BaseClientMonitor clientMonitor,
+ @Nullable BaseClientMonitor.Callback callback) {
+ this.mClientMonitor = clientMonitor;
this.mClientCallback = callback;
- state = STATE_WAITING_IN_QUEUE;
+ 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 clientMonitor + ", State: " + state;
+ return mClientMonitor + ", State: " + mState;
}
}
@@ -134,10 +151,10 @@
@Override
public void run() {
- if (operation.state != Operation.STATE_FINISHED) {
+ if (operation.mState != Operation.STATE_FINISHED) {
Slog.e(tag, "[Watchdog Triggered]: " + operation);
- operation.clientMonitor.mCallback
- .onClientFinished(operation.clientMonitor, false /* success */);
+ operation.mClientMonitor.mCallback
+ .onClientFinished(operation.mClientMonitor, false /* success */);
}
}
}
@@ -186,9 +203,9 @@
// Internal callback, notified when an operation is complete. Notifies the requester
// that the operation is complete, before performing internal scheduler work (such as
// starting the next client).
- public class InternalCallback implements ClientMonitor.Callback {
+ public class InternalCallback implements BaseClientMonitor.Callback {
@Override
- public void onClientStarted(@NonNull ClientMonitor<?> 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 ClientMonitor<?> clientMonitor, boolean success) {
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
mHandler.post(() -> {
if (mCurrentOperation == null) {
Slog.e(getTag(), "[Finishing] " + clientMonitor
@@ -205,14 +222,14 @@
return;
}
- if (clientMonitor != mCurrentOperation.clientMonitor) {
+ if (clientMonitor != mCurrentOperation.mClientMonitor) {
Slog.e(getTag(), "[Ignoring Finish] " + clientMonitor + " does not match"
- + " current: " + mCurrentOperation.clientMonitor);
+ + " current: " + mCurrentOperation.mClientMonitor);
return;
}
Slog.d(getTag(), "[Finishing] " + clientMonitor + ", success: " + success);
- mCurrentOperation.state = Operation.STATE_FINISHED;
+ mCurrentOperation.mState = Operation.STATE_FINISHED;
if (mCurrentOperation.mClientCallback != null) {
mCurrentOperation.mClientCallback.onClientFinished(clientMonitor, success);
@@ -220,7 +237,7 @@
if (mGestureAvailabilityDispatcher != null) {
mGestureAvailabilityDispatcher.markSensorActive(
- mCurrentOperation.clientMonitor.getSensorId(), false /* active */);
+ mCurrentOperation.mClientMonitor.getSensorId(), false /* active */);
}
mCurrentOperation = null;
@@ -276,11 +293,11 @@
}
mCurrentOperation = mPendingOperations.poll();
- final ClientMonitor<?> currentClient = mCurrentOperation.clientMonitor;
+ 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.
- if (mCurrentOperation.state == Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
+ if (mCurrentOperation.mState == Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
Slog.d(getTag(), "[Now Cancelling] " + mCurrentOperation);
if (!(currentClient instanceof Interruptable)) {
throw new IllegalStateException("Mis-implemented client or scheduler, "
@@ -295,9 +312,9 @@
}
if (mGestureAvailabilityDispatcher != null
- && mCurrentOperation.clientMonitor instanceof AcquisitionClient) {
+ && mCurrentOperation.mClientMonitor instanceof AcquisitionClient) {
mGestureAvailabilityDispatcher.markSensorActive(
- mCurrentOperation.clientMonitor.getSensorId(),
+ mCurrentOperation.mClientMonitor.getSensorId(),
true /* active */);
}
@@ -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.clientMonitor.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,10 +334,10 @@
// 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.clientMonitor.unableToStart();
+ halClientMonitor.unableToStart();
if (mCurrentOperation.mClientCallback != null) {
- mCurrentOperation.mClientCallback
- .onClientFinished(mCurrentOperation.clientMonitor, false /* success */);
+ mCurrentOperation.mClientCallback.onClientFinished(
+ mCurrentOperation.mClientMonitor, false /* success */);
}
// Then for each operation currently in the pending queue at the time of this
@@ -331,9 +350,11 @@
+ ", expected length: " + pendingOperationsLength);
break;
}
- operation.clientMonitor.unableToStart();
+ if (operation.isHalOperation()) {
+ ((HalClientMonitor<?>) operation.mClientMonitor).unableToStart();
+ }
if (operation.mClientCallback != null) {
- operation.mClientCallback.onClientFinished(operation.clientMonitor,
+ operation.mClientCallback.onClientFinished(operation.mClientMonitor,
false /* success */);
}
Slog.w(getTag(), "[Aborted Operation] " + operation);
@@ -347,7 +368,7 @@
} else {
Slog.d(getTag(), "[Starting] " + mCurrentOperation);
currentClient.start(getInternalCallback());
- mCurrentOperation.state = Operation.STATE_STARTED;
+ mCurrentOperation.mState = Operation.STATE_STARTED;
}
} else {
try {
@@ -356,7 +377,7 @@
Slog.e(getTag(), "Remote exception when contacting BiometricService", e);
}
Slog.d(getTag(), "Waiting for cookie before starting: " + mCurrentOperation);
- mCurrentOperation.state = Operation.STATE_WAITING_FOR_COOKIE;
+ mCurrentOperation.mState = Operation.STATE_WAITING_FOR_COOKIE;
}
}
@@ -378,13 +399,14 @@
Slog.e(getTag(), "Current operation is null");
return;
}
- if (mCurrentOperation.state != Operation.STATE_WAITING_FOR_COOKIE) {
- if (mCurrentOperation.state == Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
+ if (mCurrentOperation.mState != Operation.STATE_WAITING_FOR_COOKIE) {
+ if (mCurrentOperation.mState == Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
Slog.d(getTag(), "Operation was marked for cancellation, cancelling now: "
+ mCurrentOperation);
// This should trigger the internal onClientFinished callback, which clears the
// operation and starts the next one.
- final Interruptable interruptable = (Interruptable) mCurrentOperation.clientMonitor;
+ final Interruptable interruptable =
+ (Interruptable) mCurrentOperation.mClientMonitor;
interruptable.onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED,
0 /* vendorCode */);
return;
@@ -394,55 +416,57 @@
return;
}
}
- if (mCurrentOperation.clientMonitor.getCookie() != cookie) {
+ if (mCurrentOperation.mClientMonitor.getCookie() != cookie) {
Slog.e(getTag(), "Mismatched cookie for operation: " + mCurrentOperation
+ ", received: " + cookie);
return;
}
- if (mCurrentOperation.clientMonitor.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.clientMonitor.unableToStart();
+ final HalClientMonitor<?> halClientMonitor =
+ (HalClientMonitor<?>) mCurrentOperation.mClientMonitor;
+ halClientMonitor.unableToStart();
if (mCurrentOperation.mClientCallback != null) {
- mCurrentOperation.mClientCallback.onClientFinished(mCurrentOperation.clientMonitor,
+ mCurrentOperation.mClientCallback.onClientFinished(mCurrentOperation.mClientMonitor,
false /* success */);
}
mCurrentOperation = null;
startNextOperationIfIdle();
} else {
Slog.d(getTag(), "[Starting] Prepared client: " + mCurrentOperation);
- mCurrentOperation.state = Operation.STATE_STARTED;
- mCurrentOperation.clientMonitor.start(getInternalCallback());
+ mCurrentOperation.mState = Operation.STATE_STARTED;
+ mCurrentOperation.mClientMonitor.start(getInternalCallback());
}
}
/**
- * Adds a {@link ClientMonitor} to the pending queue
+ * Adds a {@link BaseClientMonitor} to the pending queue
*
* @param clientMonitor operation to be scheduled
*/
- public void scheduleClientMonitor(@NonNull ClientMonitor<?> clientMonitor) {
+ public void scheduleClientMonitor(@NonNull BaseClientMonitor clientMonitor) {
scheduleClientMonitor(clientMonitor, null /* clientFinishCallback */);
}
/**
- * Adds a {@link ClientMonitor} to the pending queue
+ * Adds a {@link BaseClientMonitor} to the pending queue
*
* @param clientMonitor operation to be scheduled
* @param clientCallback optional callback, invoked when the client is finished, but
* before it has been removed from the queue.
*/
- public void scheduleClientMonitor(@NonNull ClientMonitor<?> clientMonitor,
- @Nullable ClientMonitor.Callback clientCallback) {
+ 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.
for (Operation operation : mPendingOperations) {
- if (operation.clientMonitor instanceof Interruptable
- && operation.state != Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
+ if (operation.mClientMonitor instanceof Interruptable
+ && operation.mState != Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
Slog.d(getTag(), "New client incoming, marking pending client as canceling: "
- + operation.clientMonitor);
- operation.state = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
+ + operation.mClientMonitor);
+ operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
}
}
@@ -451,8 +475,8 @@
+ ", new queue size: " + mPendingOperations.size());
// If the current operation is cancellable, start the cancellation process.
- if (mCurrentOperation != null && mCurrentOperation.clientMonitor instanceof Interruptable
- && mCurrentOperation.state == Operation.STATE_STARTED) {
+ if (mCurrentOperation != null && mCurrentOperation.mClientMonitor instanceof Interruptable
+ && mCurrentOperation.mState == Operation.STATE_STARTED) {
Slog.d(getTag(), "[Cancelling Interruptable]: " + mCurrentOperation);
cancelInternal(mCurrentOperation);
}
@@ -465,25 +489,25 @@
Slog.e(getTag(), "cancelInternal invoked on non-current operation: " + operation);
return;
}
- if (!(operation.clientMonitor instanceof Interruptable)) {
+ if (!(operation.mClientMonitor instanceof Interruptable)) {
Slog.w(getTag(), "Operation not interruptable: " + operation);
return;
}
- if (operation.state == Operation.STATE_STARTED_CANCELING) {
+ if (operation.mState == Operation.STATE_STARTED_CANCELING) {
Slog.w(getTag(), "Cancel already invoked for operation: " + operation);
return;
}
- if (operation.state == Operation.STATE_WAITING_FOR_COOKIE) {
+ if (operation.mState == Operation.STATE_WAITING_FOR_COOKIE) {
Slog.w(getTag(), "Skipping cancellation for non-started operation: " + operation);
// We can set it to null immediately, since the HAL was never notified to start.
mCurrentOperation = null;
startNextOperationIfIdle();
return;
}
- Slog.d(getTag(), "[Cancelling] Current client: " + operation.clientMonitor);
- final Interruptable interruptable = (Interruptable) operation.clientMonitor;
+ Slog.d(getTag(), "[Cancelling] Current client: " + operation.mClientMonitor);
+ final Interruptable interruptable = (Interruptable) operation.mClientMonitor;
interruptable.cancel();
- operation.state = Operation.STATE_STARTED_CANCELING;
+ operation.mState = Operation.STATE_STARTED_CANCELING;
// Add a watchdog. If the HAL does not acknowledge within the timeout, we will
// forcibly finish this client.
@@ -500,8 +524,8 @@
Slog.e(getTag(), "Unable to cancel enrollment, null operation");
return;
}
- final boolean isEnrolling = mCurrentOperation.clientMonitor instanceof EnrollClient;
- final boolean tokenMatches = mCurrentOperation.clientMonitor.getToken() == token;
+ final boolean isEnrolling = mCurrentOperation.mClientMonitor instanceof EnrollClient;
+ final boolean tokenMatches = mCurrentOperation.mClientMonitor.getToken() == token;
if (!isEnrolling || !tokenMatches) {
Slog.w(getTag(), "Not cancelling enrollment, isEnrolling: " + isEnrolling
+ " tokenMatches: " + tokenMatches);
@@ -521,8 +545,8 @@
return;
}
final boolean isAuthenticating =
- mCurrentOperation.clientMonitor instanceof AuthenticationConsumer;
- final boolean tokenMatches = mCurrentOperation.clientMonitor.getToken() == token;
+ mCurrentOperation.mClientMonitor instanceof AuthenticationConsumer;
+ final boolean tokenMatches = mCurrentOperation.mClientMonitor.getToken() == token;
if (!isAuthenticating || !tokenMatches) {
Slog.w(getTag(), "Not cancelling authentication"
+ ", current operation : " + mCurrentOperation
@@ -536,11 +560,11 @@
/**
* @return the current operation
*/
- public ClientMonitor<?> getCurrentClient() {
+ public BaseClientMonitor getCurrentClient() {
if (mCurrentOperation == null) {
return null;
}
- return mCurrentOperation.clientMonitor;
+ return mCurrentOperation.mClientMonitor;
}
public int getCurrentPendingCount() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnumerateConsumer.java b/services/core/java/com/android/server/biometrics/sensors/EnumerateConsumer.java
index 8ad9e6a..07a4d11 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnumerateConsumer.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnumerateConsumer.java
@@ -19,8 +19,8 @@
import android.hardware.biometrics.BiometricAuthenticator;
/**
- * Interface that {@link ClientMonitor} subclasses eligible/interested in enumerate callbacks should
- * implement.
+ * Interface that {@link BaseClientMonitor} subclasses eligible/interested in enumerate callbacks
+ * should implement.
*/
public interface EnumerateConsumer {
/**
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 bac944f..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 ClientMonitor<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 e738d17..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 ClientMonitor<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 ClientMonitor<T> mCurrentTask;
+ private BaseClientMonitor mCurrentTask;
private final Callback mEnumerateCallback = new Callback() {
@Override
- public void onClientFinished(@NonNull ClientMonitor<?> 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 ClientMonitor<?> 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 e07f712..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 ClientMonitor<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/Interruptable.java b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
index 28e0117..70d9b26 100644
--- a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
+++ b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
@@ -19,7 +19,7 @@
import android.annotation.NonNull;
/**
- * Interface that {@link ClientMonitor} subclasses eligible/interested in error callbacks should
+ * Interface that {@link BaseClientMonitor} subclasses eligible/interested in error callbacks should
* implement.
*/
public interface Interruptable {
@@ -37,10 +37,10 @@
/**
* Notifies the client that it needs to finish before
- * {@link ClientMonitor#start(ClientMonitor.Callback)} was invoked. This usually happens
+ * {@link BaseClientMonitor#start(BaseClientMonitor.Callback)} was invoked. This usually happens
* if the client is still waiting in the pending queue and got notified that a subsequent
* operation is preempting it.
* @param callback invoked when the operation is completed.
*/
- void cancelWithoutStarting(@NonNull ClientMonitor.Callback callback);
+ void cancelWithoutStarting(@NonNull BaseClientMonitor.Callback callback);
}
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 fe946cb..630e5ea 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
@@ -20,6 +20,9 @@
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.IInvalidationCallback;
+import android.os.RemoteException;
+import android.util.Slog;
import java.util.Map;
@@ -28,24 +31,31 @@
* {@link InvalidationRequesterClient} for more info.
*/
public abstract class InvalidationClient<S extends BiometricAuthenticator.Identifier, T>
- extends ClientMonitor<T> {
+ extends HalClientMonitor<T> {
- private final BiometricUtils<S> mUtils;
- private final Map<Integer, Long> mAuthenticatorIds;
+ private static final String TAG = "InvalidationClient";
+
+ @NonNull private final Map<Integer, Long> mAuthenticatorIds;
+ @NonNull private final IInvalidationCallback mInvalidationCallback;
public InvalidationClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
- int userId, int sensorId, @NonNull BiometricUtils<S> utils,
- @NonNull Map<Integer, Long> authenticatorIds) {
+ int userId, int sensorId, @NonNull Map<Integer, Long> authenticatorIds,
+ @NonNull IInvalidationCallback callback) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId,
context.getOpPackageName(), 0 /* cookie */, sensorId,
BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN);
- mUtils = utils;
mAuthenticatorIds = authenticatorIds;
+ mInvalidationCallback = callback;
}
public void onAuthenticatorIdInvalidated(long newAuthenticatorId) {
mAuthenticatorIds.put(getTargetUserId(), newAuthenticatorId);
+ try {
+ mInvalidationCallback.onCompleted();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
mCallback.onClientFinished(this, true /* success */);
}
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 d95fa23..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 ClientMonitor<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 f79abd5..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 ClientMonitor<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/RemovalConsumer.java b/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java
index 0aba7e4..82fa1647a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java
@@ -20,8 +20,8 @@
import android.hardware.biometrics.BiometricAuthenticator;
/**
- * Interface that {@link ClientMonitor} subclasses eligible/interested in removal callbacks should
- * implement.
+ * Interface that {@link BaseClientMonitor} subclasses eligible/interested in removal callbacks
+ * should implement.
*/
public interface RemovalConsumer {
/**
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 5deb8fa..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 ClientMonitor<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 c27b6e5..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.ClientMonitor;
+import com.android.server.biometrics.sensors.HalClientMonitor;
import java.util.Map;
-class FaceGetAuthenticatorIdClient extends ClientMonitor<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/FaceInvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java
index 9c6438e..855ee1d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java
@@ -18,13 +18,13 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.Face;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.sensors.InvalidationClient;
-import com.android.server.biometrics.sensors.face.FaceUtils;
import java.util.Map;
@@ -33,8 +33,8 @@
public FaceInvalidationClient(@NonNull Context context,
@NonNull LazyDaemon<ISession> lazyDaemon, int userId, int sensorId,
- @NonNull FaceUtils utils, @NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, userId, sensorId, utils, authenticatorIds);
+ @NonNull Map<Integer, Long> authenticatorIds, @NonNull IInvalidationCallback callback) {
+ super(context, lazyDaemon, userId, sensorId, authenticatorIds, callback);
}
@Override
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 cec1cb8..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
@@ -20,10 +20,10 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
-import android.app.IActivityTaskManager;
import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.SensorProps;
@@ -44,8 +44,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
-import com.android.server.biometrics.sensors.ClientMonitor;
+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 ClientMonitor.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 ClientMonitor<?> 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 ClientMonitor<?> 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,8 +191,8 @@
mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
}
- private void scheduleForSensor(int sensorId, @NonNull ClientMonitor<?> client,
- ClientMonitor.Callback callback) {
+ private void scheduleForSensor(int sensorId, @NonNull BaseClientMonitor client,
+ BaseClientMonitor.Callback callback) {
if (!mSensors.contains(sensorId)) {
throw new IllegalStateException("Unable to schedule client: " + client
+ " for sensor: " + sensorId);
@@ -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);
@@ -269,6 +286,32 @@
}
@Override
+ public void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
+ @NonNull IInvalidationCallback callback) {
+ mHandler.post(() -> {
+ final IFace daemon = getHalInstance();
+ if (daemon == null) {
+ Slog.e(getTag(), "Null daemon during scheduleInvalidateAuthenticatorId: "
+ + sensorId);
+ return;
+ }
+
+ try {
+ if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+ createNewSessionWithoutHandler(daemon, sensorId, userId);
+ }
+
+ final FaceInvalidationClient client = new FaceInvalidationClient(mContext,
+ mSensors.get(sensorId).getLazySession(), userId, sensorId,
+ mSensors.get(sensorId).getAuthenticatorIds(), callback);
+ mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Remote exception", e);
+ }
+ });
+ }
+
+ @Override
public int getLockoutModeForUser(int sensorId, int userId) {
return mSensors.get(sensorId).getLockoutCache().getLockoutModeForUser(userId);
}
@@ -362,12 +405,13 @@
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, FaceUtils.getInstance(sensorId), disabledFeatures,
ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser);
- scheduleForSensor(sensorId, client, new ClientMonitor.Callback() {
+ scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() {
@Override
- public void onClientFinished(@NonNull ClientMonitor<?> 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 5b1f546..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.ClientMonitor;
+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 ClientMonitor<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 f9e3106..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
@@ -45,9 +45,10 @@
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
-import com.android.server.biometrics.sensors.ClientMonitor;
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 ClientMonitor.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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> client = mScheduler.getCurrentClient();
+ final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof FaceGetAuthenticatorIdClient)) {
Slog.e(mTag, "onAuthenticatorIdRetrieved for wrong consumer: "
+ Utils.getClientName(client));
@@ -376,7 +377,17 @@
@Override
public void onAuthenticatorIdInvalidated(long newAuthenticatorId) {
- // TODO(b/159667191)
+ mHandler.post(() -> {
+ final BaseClientMonitor client = mScheduler.getCurrentClient();
+ if (!(client instanceof FaceInvalidationClient)) {
+ Slog.e(mTag, "onAuthenticatorIdInvalidated for wrong consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final FaceInvalidationClient invalidationClient = (FaceInvalidationClient) client;
+ invalidationClient.onAuthenticatorIdInvalidated(newAuthenticatorId);
+ });
}
}
@@ -400,7 +411,7 @@
};
}
- @NonNull ClientMonitor.LazyDaemon<ISession> getLazySession() {
+ @NonNull HalClientMonitor.LazyDaemon<ISession> getLazySession() {
return mLazySession;
}
@@ -481,7 +492,7 @@
public void binderDied() {
Slog.e(mTag, "Binder died");
mHandler.post(() -> {
- final ClientMonitor<?> 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 10b12cb..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
@@ -58,10 +58,11 @@
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
-import com.android.server.biometrics.sensors.ClientMonitor;
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 ClientMonitor.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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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
@@ -528,9 +529,9 @@
final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), opPackageName,
mSensorId, mCurrentChallengeOwner);
- mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() {
+ mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
- public void onClientStarted(@NonNull ClientMonitor<?> clientMonitor) {
+ public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
if (client != clientMonitor) {
Slog.e(TAG, "scheduleGenerateChallenge onClientStarted, mismatched client."
+ " Expecting: " + client + ", received: " + clientMonitor);
@@ -557,9 +558,9 @@
final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
mLazyDaemon, token, opPackageName, mSensorId);
- mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() {
+ mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
- public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor,
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
if (client != clientMonitor) {
Slog.e(TAG, "scheduleRevokeChallenge, mismatched client."
@@ -613,9 +614,9 @@
opPackageName, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
ENROLL_TIMEOUT_SEC, surfaceHandle, mSensorId);
- mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() {
+ mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
- public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor,
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
if (success) {
// Update authenticatorIds
@@ -724,10 +725,10 @@
final int faceId = faces.get(0).getBiometricId();
final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mLazyDaemon,
token, listener, userId, opPackageName, mSensorId, feature, faceId);
- mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() {
+ mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
public void onClientFinished(
- @NonNull ClientMonitor<?> 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
@@ -862,9 +863,10 @@
final FaceUpdateActiveUserClient client = new FaceUpdateActiveUserClient(mContext,
mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId, mCurrentUserId,
hasEnrolled, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() {
+ mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
- public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, boolean success) {
+ 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 e25bb81..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.ClientMonitor;
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 ClientMonitor<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 8df9b9f..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.ClientMonitor;
+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 ClientMonitor<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 0e20728..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.ClientMonitor;
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 ClientMonitor<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 22275e5..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.ClientMonitor;
+import com.android.server.biometrics.sensors.HalClientMonitor;
import java.io.File;
import java.util.Map;
-public class FaceUpdateActiveUserClient extends ClientMonitor<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/Udfps.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
index 0bf107a..0aa112f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
@@ -16,9 +16,11 @@
package com.android.server.biometrics.sensors.fingerprint;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
+
/**
* Interface for under-display fingerprint sensors.
- * {@link com.android.server.biometrics.sensors.ClientMonitor} subclass that require knowledge of
+ * {@link BaseClientMonitor} subclass that require knowledge of
* finger position (e.g. enroll, authenticate) should implement this.
*/
public interface Udfps {
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 2ad1fa3..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.ClientMonitor;
+import com.android.server.biometrics.sensors.HalClientMonitor;
import java.util.Map;
-class FingerprintGetAuthenticatorIdClient extends ClientMonitor<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/FingerprintInvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java
index 3d07334..80d1a0f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java
@@ -18,13 +18,13 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.fingerprint.Fingerprint;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.sensors.InvalidationClient;
-import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import java.util.Map;
@@ -33,8 +33,8 @@
public FingerprintInvalidationClient(@NonNull Context context,
@NonNull LazyDaemon<ISession> lazyDaemon, int userId, int sensorId,
- @NonNull FingerprintUtils utils, @NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, userId, sensorId, utils, authenticatorIds);
+ @NonNull Map<Integer, Long> authenticatorIds, @NonNull IInvalidationCallback callback) {
+ super(context, lazyDaemon, userId, sensorId, authenticatorIds, callback);
}
@Override
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 a03deba..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
@@ -23,6 +23,7 @@
import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorProps;
@@ -43,8 +44,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
-import com.android.server.biometrics.sensors.ClientMonitor;
+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;
@@ -73,7 +76,7 @@
@NonNull private final String mHalInstanceName;
@NonNull @VisibleForTesting
final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
- @NonNull private final ClientMonitor.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;
@@ -87,7 +90,7 @@
public void onTaskStackChanged() {
mHandler.post(() -> {
for (int i = 0; i < mSensors.size(); i++) {
- final ClientMonitor<?> 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);
@@ -186,7 +189,7 @@
return mDaemon;
}
- private void scheduleForSensor(int sensorId, @NonNull ClientMonitor<?> client) {
+ private void scheduleForSensor(int sensorId, @NonNull BaseClientMonitor client) {
if (!mSensors.contains(sensorId)) {
throw new IllegalStateException("Unable to schedule client: " + client
+ " for sensor: " + sensorId);
@@ -194,8 +197,8 @@
mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
}
- private void scheduleForSensor(int sensorId, @NonNull ClientMonitor<?> client,
- ClientMonitor.Callback callback) {
+ private void scheduleForSensor(int sensorId, @NonNull BaseClientMonitor client,
+ BaseClientMonitor.Callback callback) {
if (!mSensors.contains(sensorId)) {
throw new IllegalStateException("Unable to schedule client: " + client
+ " for sensor: " + sensorId);
@@ -210,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
@@ -266,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(() -> {
@@ -370,12 +388,13 @@
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
mUdfpsOverlayController, maxTemplatesPerUser, shouldLogMetrics);
- scheduleForSensor(sensorId, client, new ClientMonitor.Callback() {
+ scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() {
@Override
- public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor,
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
if (success) {
scheduleLoadAuthenticatorIdsForUser(sensorId, userId);
+ scheduleInvalidationRequest(sensorId, userId);
}
}
});
@@ -542,6 +561,33 @@
}
@Override
+ public void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
+ @NonNull IInvalidationCallback callback) {
+ mHandler.post(() -> {
+ final IFingerprint daemon = getHalInstance();
+ if (daemon == null) {
+ Slog.e(getTag(), "Null daemon during scheduleInvalidateAuthenticatorId: "
+ + sensorId);
+ return;
+ }
+
+ try {
+ if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+ createNewSessionWithoutHandler(daemon, sensorId, userId);
+ }
+
+ final FingerprintInvalidationClient client =
+ new FingerprintInvalidationClient(mContext,
+ mSensors.get(sensorId).getLazySession(), userId, sensorId,
+ mSensors.get(sensorId).getAuthenticatorIds(), callback);
+ mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Remote exception", e);
+ }
+ });
+ }
+
+ @Override
public int getLockoutModeForUser(int sensorId, int userId) {
return mSensors.get(sensorId).getLockoutCache().getLockoutModeForUser(userId);
}
@@ -553,7 +599,8 @@
@Override
public void onPointerDown(int sensorId, int x, int y, float minor, float major) {
- final ClientMonitor<?> client = mSensors.get(sensorId).getScheduler().getCurrentClient();
+ final BaseClientMonitor client =
+ mSensors.get(sensorId).getScheduler().getCurrentClient();
if (!(client instanceof Udfps)) {
Slog.e(getTag(), "onPointerDown received during client: " + client);
return;
@@ -564,7 +611,8 @@
@Override
public void onPointerUp(int sensorId) {
- final ClientMonitor<?> client = mSensors.get(sensorId).getScheduler().getCurrentClient();
+ final BaseClientMonitor client =
+ mSensors.get(sensorId).getScheduler().getCurrentClient();
if (!(client instanceof Udfps)) {
Slog.e(getTag(), "onPointerUp received during client: " + client);
return;
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 1f1d19d..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.ClientMonitor;
+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 ClientMonitor<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 bb0f983..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
@@ -45,9 +45,10 @@
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
-import com.android.server.biometrics.sensors.ClientMonitor;
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 ClientMonitor.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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> client = mScheduler.getCurrentClient();
+ final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof FingerprintGetAuthenticatorIdClient)) {
Slog.e(mTag, "onAuthenticatorIdRetrieved for wrong consumer: "
+ Utils.getClientName(client));
@@ -389,7 +390,18 @@
@Override
public void onAuthenticatorIdInvalidated(long newAuthenticatorId) {
- // TODO(159667191)
+ mHandler.post(() -> {
+ final BaseClientMonitor client = mScheduler.getCurrentClient();
+ if (!(client instanceof FingerprintInvalidationClient)) {
+ Slog.e(mTag, "onAuthenticatorIdInvalidated for wrong consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final FingerprintInvalidationClient invalidationClient =
+ (FingerprintInvalidationClient) client;
+ invalidationClient.onAuthenticatorIdInvalidated(newAuthenticatorId);
+ });
}
}
@@ -413,7 +425,7 @@
};
}
- @NonNull ClientMonitor.LazyDaemon<ISession> getLazySession() {
+ @NonNull HalClientMonitor.LazyDaemon<ISession> getLazySession() {
return mLazySession;
}
@@ -494,7 +506,7 @@
public void binderDied() {
Slog.e(mTag, "Binder died");
mHandler.post(() -> {
- final ClientMonitor<?> 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 7989e6e4..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
@@ -58,10 +58,11 @@
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
-import com.android.server.biometrics.sensors.ClientMonitor;
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 ClientMonitor.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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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;
@@ -484,9 +485,10 @@
new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId,
mContext.getOpPackageName(), mSensorProperties.sensorId, mCurrentUserId,
hasEnrolled, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() {
+ mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
- public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, boolean success) {
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
if (success) {
mCurrentUserId = targetUserId;
}
@@ -557,9 +559,9 @@
hardwareAuthToken, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController,
shouldLogMetrics);
- mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() {
+ mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
- public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor,
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
if (success) {
// Update authenticatorIds
@@ -681,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 ClientMonitor<?> client = mScheduler.getCurrentClient();
+ final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof Udfps)) {
Slog.w(TAG, "onFingerDown received during client: " + client);
return;
@@ -697,7 +699,7 @@
@Override
public void onPointerUp(int sensorId) {
- final ClientMonitor<?> 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 791d224..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
@@ -40,8 +40,8 @@
import com.android.internal.R;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
-import com.android.server.biometrics.sensors.ClientMonitor;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
@@ -146,14 +146,14 @@
class TestableInternalCallback extends InternalCallback {
@Override
- public void onClientStarted(ClientMonitor<?> clientMonitor) {
+ public void onClientStarted(BaseClientMonitor clientMonitor) {
super.onClientStarted(clientMonitor);
Slog.d(TAG, "Client started: " + clientMonitor);
mFingerprint21.setDebugMessage("Started: " + clientMonitor);
}
@Override
- public void onClientFinished(ClientMonitor<?> clientMonitor, boolean success) {
+ public void onClientFinished(BaseClientMonitor clientMonitor, boolean success) {
super.onClientFinished(clientMonitor, success);
Slog.d(TAG, "Client finished: " + clientMonitor);
mFingerprint21.setDebugMessage("Finished: " + clientMonitor);
@@ -175,7 +175,7 @@
/**
* All of the mocking/testing should happen in here. This way we don't need to modify the
- * {@link com.android.server.biometrics.sensors.ClientMonitor} implementations and can run the
+ * {@link BaseClientMonitor} implementations and can run the
* real path there.
*/
private static class MockHalResultController extends HalResultController {
@@ -233,7 +233,7 @@
public void onAuthenticated(long deviceId, int fingerId, int groupId,
ArrayList<Byte> token) {
mHandler.post(() -> {
- final ClientMonitor<?> 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 ClientMonitor<?> 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 ClientMonitor<?> 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 00e2413..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.ClientMonitor;
+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 ClientMonitor<IBiometricsFingerprint> {
+public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometricsFingerprint> {
private static final String TAG = "FingerprintUpdateActiveUserClient";
private static final String FP_DATA_DIR = "fpdata";
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 2a81426..40d2073 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -57,6 +57,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -466,8 +467,13 @@
streamProtos[i].firstCaptureLatencyMillis = streamStats.getStartLatencyMs();
streamProtos[i].maxHalBuffers = streamStats.getMaxHalBuffers();
streamProtos[i].maxAppBuffers = streamStats.getMaxAppBuffers();
+ streamProtos[i].histogramType = streamStats.getHistogramType();
+ streamProtos[i].histogramBins = streamStats.getHistogramBins();
+ streamProtos[i].histogramCounts = streamStats.getHistogramCounts();
if (CameraServiceProxy.DEBUG) {
+ String histogramTypeName =
+ cameraHistogramTypeToString(streamProtos[i].histogramType);
Slog.v(TAG, "Stream " + i + ": width " + streamProtos[i].width
+ ", height " + streamProtos[i].height
+ ", format " + streamProtos[i].format
@@ -478,7 +484,12 @@
+ ", firstCaptureLatencyMillis "
+ streamProtos[i].firstCaptureLatencyMillis
+ ", maxHalBuffers " + streamProtos[i].maxHalBuffers
- + ", maxAppBuffers " + streamProtos[i].maxAppBuffers);
+ + ", maxAppBuffers " + streamProtos[i].maxAppBuffers
+ + ", histogramType " + histogramTypeName
+ + ", histogramBins "
+ + Arrays.toString(streamProtos[i].histogramBins)
+ + ", histogramCounts "
+ + Arrays.toString(streamProtos[i].histogramCounts));
}
}
}
@@ -797,4 +808,14 @@
return "CAMERA_FACING_UNKNOWN";
}
+ private static String cameraHistogramTypeToString(int cameraHistogramType) {
+ switch (cameraHistogramType) {
+ case CameraStreamStats.HISTOGRAM_TYPE_CAPTURE_LATENCY:
+ return "HISTOGRAM_TYPE_CAPTURE_LATENCY";
+ default:
+ break;
+ }
+ return "HISTOGRAM_TYPE_UNKNOWN";
+ }
+
}
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index 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/PacProxyInstaller.java b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
index aadaf4d..5dc8c1a 100644
--- a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
+++ b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
@@ -16,6 +16,7 @@
package com.android.server.connectivity;
+import android.annotation.NonNull;
import android.annotation.WorkerThread;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -71,10 +72,6 @@
private static final int DELAY_LONG = 4;
private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
- // Return values for #setCurrentProxyScriptUrl
- public static final boolean DONT_SEND_BROADCAST = false;
- public static final boolean DO_SEND_BROADCAST = true;
-
private String mCurrentPac;
@GuardedBy("mProxyLock")
private volatile Uri mPacUrl = Uri.EMPTY;
@@ -93,7 +90,7 @@
private volatile boolean mHasSentBroadcast;
private volatile boolean mHasDownloaded;
- private Handler mConnectivityHandler;
+ private final Handler mConnectivityHandler;
private final int mProxyMessage;
/**
@@ -102,6 +99,13 @@
private final Object mProxyLock = new Object();
/**
+ * Lock ensuring consistency between the values of mHasSentBroadcast, mHasDownloaded, the
+ * last URL and port, and the broadcast message being sent with the correct arguments.
+ * TODO : this should probably protect all instances of these variables
+ */
+ private final Object mBroadcastStateLock = new Object();
+
+ /**
* Runnable to download PAC script.
* The behavior relies on the assumption it always runs on mNetThread to guarantee that the
* latest data fetched from mPacUrl is stored in mProxyService.
@@ -146,7 +150,7 @@
}
}
- public PacProxyInstaller(Context context, Handler handler, int proxyMessage) {
+ public PacProxyInstaller(@NonNull Context context, @NonNull Handler handler, int proxyMessage) {
mContext = context;
mLastPort = -1;
final HandlerThread netThread = new HandlerThread("android.pacproxyinstaller",
@@ -176,31 +180,27 @@
* PacProxyInstaller will trigger a new broadcast when it is ready.
*
* @param proxy Proxy information that is about to be broadcast.
- * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST
*/
- public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
- if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
- if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
- // Allow to send broadcast, nothing to do.
- return DO_SEND_BROADCAST;
- }
- mPacUrl = proxy.getPacFileUrl();
- mCurrentDelay = DELAY_1;
- mHasSentBroadcast = false;
- mHasDownloaded = false;
- getAlarmManager().cancel(mPacRefreshIntent);
- bind();
- return DONT_SEND_BROADCAST;
- } else {
- getAlarmManager().cancel(mPacRefreshIntent);
- synchronized (mProxyLock) {
- mPacUrl = Uri.EMPTY;
- mCurrentPac = null;
- if (mProxyService != null) {
- unbind();
+ public void setCurrentProxyScriptUrl(@NonNull ProxyInfo proxy) {
+ synchronized (mBroadcastStateLock) {
+ if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
+ if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) return;
+ mPacUrl = proxy.getPacFileUrl();
+ mCurrentDelay = DELAY_1;
+ mHasSentBroadcast = false;
+ mHasDownloaded = false;
+ getAlarmManager().cancel(mPacRefreshIntent);
+ bind();
+ } else {
+ getAlarmManager().cancel(mPacRefreshIntent);
+ synchronized (mProxyLock) {
+ mPacUrl = Uri.EMPTY;
+ mCurrentPac = null;
+ if (mProxyService != null) {
+ unbind();
+ }
}
}
- return DO_SEND_BROADCAST;
}
}
@@ -275,6 +275,7 @@
getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
}
+ @GuardedBy("mProxyLock")
private void setCurrentProxyScript(String script) {
if (mProxyService == null) {
Log.e(TAG, "setCurrentProxyScript: no proxy service");
@@ -347,6 +348,9 @@
public void setProxyPort(int port) {
if (mLastPort != -1) {
// Always need to send if port changed
+ // TODO: Here lacks synchronization because this write cannot
+ // guarantee that it's visible from sendProxyIfNeeded() when
+ // it's called by a Runnable which is post by mNetThread.
mHasSentBroadcast = false;
}
mLastPort = port;
@@ -386,13 +390,15 @@
mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
}
- private synchronized void sendProxyIfNeeded() {
- if (!mHasDownloaded || (mLastPort == -1)) {
- return;
- }
- if (!mHasSentBroadcast) {
- sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
- mHasSentBroadcast = true;
+ private void sendProxyIfNeeded() {
+ synchronized (mBroadcastStateLock) {
+ if (!mHasDownloaded || (mLastPort == -1)) {
+ return;
+ }
+ if (!mHasSentBroadcast) {
+ sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
+ mHasSentBroadcast = true;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index d83ff83..b618d2b 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -226,9 +226,9 @@
final ProxyInfo defaultProxy = getDefaultProxy();
final ProxyInfo proxyInfo = null != defaultProxy ?
defaultProxy : ProxyInfo.buildDirectProxy("", 0, Collections.emptyList());
+ mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo);
- if (mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo)
- == PacProxyInstaller.DONT_SEND_BROADCAST) {
+ if (!shouldSendBroadcast(proxyInfo)) {
return;
}
if (DBG) Log.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
@@ -244,6 +244,13 @@
}
}
+ private boolean shouldSendBroadcast(ProxyInfo proxy) {
+ if (Uri.EMPTY.equals(proxy.getPacFileUrl())) return false;
+ if (proxy.getPacFileUrl().equals(proxy.getPacFileUrl())
+ && (proxy.getPort() > 0)) return true;
+ return true;
+ }
+
/**
* Sets the global proxy in memory. Also writes the values to the global settings of the device.
*
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 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/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 52947c0..b427bd0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -40,6 +40,7 @@
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiHotplugEvent;
import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.IHdmiCecSettingChangeListener;
import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlService;
@@ -74,6 +75,7 @@
import android.provider.Settings.Global;
import android.sysprop.HdmiProperties;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
@@ -308,6 +310,11 @@
private final ArrayList<VendorCommandListenerRecord> mVendorCommandListenerRecords =
new ArrayList<>();
+ // List of records for CEC setting change listener to handle the caller killed in action.
+ @GuardedBy("mLock")
+ private final ArrayMap<String, RemoteCallbackList<IHdmiCecSettingChangeListener>>
+ mHdmiCecSettingChangeListenerRecords = new ArrayMap<>();
+
@GuardedBy("mLock")
private InputChangeListenerRecord mInputChangeListenerRecord;
@@ -2240,6 +2247,20 @@
}
@Override
+ public void addCecSettingChangeListener(String name,
+ final IHdmiCecSettingChangeListener listener) {
+ enforceAccessPermission();
+ HdmiControlService.this.addCecSettingChangeListener(name, listener);
+ }
+
+ @Override
+ public void removeCecSettingChangeListener(String name,
+ final IHdmiCecSettingChangeListener listener) {
+ enforceAccessPermission();
+ HdmiControlService.this.removeCecSettingChangeListener(name, listener);
+ }
+
+ @Override
public List<String> getUserCecSettings() {
initBinderCall();
long token = Binder.clearCallingIdentity();
@@ -3468,4 +3489,53 @@
protected HdmiCecConfig getHdmiCecConfig() {
return mHdmiCecConfig;
}
+
+ private HdmiCecConfig.SettingChangeListener mSettingChangeListener =
+ new HdmiCecConfig.SettingChangeListener() {
+ @Override
+ public void onChange(String name) {
+ synchronized (mLock) {
+ if (!mHdmiCecSettingChangeListenerRecords.containsKey(name)) {
+ return;
+ }
+ mHdmiCecSettingChangeListenerRecords.get(name).broadcast(listener -> {
+ invokeCecSettingChangeListenerLocked(name, listener);
+ });
+ }
+ }
+ };
+
+ private void addCecSettingChangeListener(String name,
+ final IHdmiCecSettingChangeListener listener) {
+ synchronized (mLock) {
+ if (!mHdmiCecSettingChangeListenerRecords.containsKey(name)) {
+ mHdmiCecSettingChangeListenerRecords.put(name, new RemoteCallbackList<>());
+ mHdmiCecConfig.registerChangeListener(name, mSettingChangeListener);
+ }
+ mHdmiCecSettingChangeListenerRecords.get(name).register(listener);
+ }
+ }
+
+ private void removeCecSettingChangeListener(String name,
+ final IHdmiCecSettingChangeListener listener) {
+ synchronized (mLock) {
+ if (!mHdmiCecSettingChangeListenerRecords.containsKey(name)) {
+ return;
+ }
+ mHdmiCecSettingChangeListenerRecords.get(name).unregister(listener);
+ if (mHdmiCecSettingChangeListenerRecords.get(name).getRegisteredCallbackCount() == 0) {
+ mHdmiCecSettingChangeListenerRecords.remove(name);
+ mHdmiCecConfig.removeChangeListener(name, mSettingChangeListener);
+ }
+ }
+ }
+
+ private void invokeCecSettingChangeListenerLocked(String name,
+ final IHdmiCecSettingChangeListener listener) {
+ try {
+ listener.onChange(name);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report setting change", e);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 50e50bc..23c70ee 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -235,10 +235,6 @@
IInputFilter mInputFilter; // guarded by mInputFilterLock
InputFilterHost mInputFilterHost; // guarded by mInputFilterLock
- private final Object mGestureMonitorPidsLock = new Object();
- @GuardedBy("mGestureMonitorPidsLock")
- private final ArrayMap<IBinder, Integer> mGestureMonitorPidsByToken = new ArrayMap<>();
-
// The associations of input devices to displays by port. Maps from input device port (String)
// to display id (int). Currently only accessed by InputReader.
private final Map<String, Integer> mStaticAssociations;
@@ -640,9 +636,6 @@
InputChannel inputChannel = nativeCreateInputMonitor(
mPtr, displayId, true /*isGestureMonitor*/, inputChannelName, pid);
InputMonitorHost host = new InputMonitorHost(inputChannel.getToken());
- synchronized (mGestureMonitorPidsLock) {
- mGestureMonitorPidsByToken.put(inputChannel.getToken(), pid);
- }
return new InputMonitor(inputChannel, host);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -666,9 +659,6 @@
if (connectionToken == null) {
throw new IllegalArgumentException("connectionToken must not be null.");
}
- synchronized (mGestureMonitorPidsLock) {
- mGestureMonitorPidsByToken.remove(connectionToken);
- }
nativeRemoveInputChannel(mPtr, connectionToken);
}
@@ -1866,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() };
@@ -1892,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");
}
}
}
@@ -2069,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
@@ -2167,7 +2162,6 @@
if (dumpStr != null) {
pw.println(dumpStr);
dumpAssociations(pw);
- dumpGestureMonitorPidsByToken(pw);
}
}
@@ -2191,19 +2185,6 @@
}
}
- private void dumpGestureMonitorPidsByToken(PrintWriter pw) {
- synchronized (mGestureMonitorPidsLock) {
- if (!mGestureMonitorPidsByToken.isEmpty()) {
- pw.println("Gesture monitor pids by token:");
- for (int i = 0; i < mGestureMonitorPidsByToken.size(); i++) {
- pw.print(" " + i + ": ");
- pw.print(" token: " + mGestureMonitorPidsByToken.keyAt(i));
- pw.println(" pid: " + mGestureMonitorPidsByToken.valueAt(i));
- }
- }
- }
- }
-
private boolean checkCallingPermission(String permission, String func) {
// Quick check: if the calling permission is me, it's all okay.
if (Binder.getCallingPid() == Process.myPid()) {
@@ -2226,7 +2207,6 @@
public void monitor() {
synchronized (mInputFilterLock) { }
synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */}
- synchronized (mGestureMonitorPidsLock) { /* Test if blocked by gesture monitor pids lock */}
synchronized (mLidSwitchLock) { /* Test if blocked by lid switch lock. */ }
nativeMonitor(mPtr);
}
@@ -2296,9 +2276,6 @@
// Native callback.
private void notifyInputChannelBroken(IBinder token) {
- synchronized (mGestureMonitorPidsLock) {
- mGestureMonitorPidsByToken.remove(token);
- }
mWindowManagerCallbacks.notifyInputChannelBroken(token);
}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index e8052289..83b6eca 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -807,22 +807,7 @@
@Override
public LocationTime getGnssTimeMillis() {
- synchronized (mLock) {
- LocationProviderManager gpsManager = getLocationProviderManager(GPS_PROVIDER);
- if (gpsManager == null) {
- return null;
- }
-
- Location location = gpsManager.getLastLocationUnsafe(UserHandle.USER_ALL,
- PERMISSION_FINE, false, Long.MAX_VALUE);
- if (location == null) {
- return null;
- }
-
- long currentNanos = SystemClock.elapsedRealtimeNanos();
- long deltaMs = NANOSECONDS.toMillis(location.getElapsedRealtimeAgeNanos(currentNanos));
- return new LocationTime(location.getTime() + deltaMs, currentNanos);
- }
+ return mLocalService.getGnssTimeMillis();
}
@Override
@@ -1292,6 +1277,25 @@
mGnssManagerService.sendNiResponse(notifId, userResponse);
}
}
+
+ @Override
+ public @Nullable LocationTime getGnssTimeMillis() {
+ LocationProviderManager gpsManager = getLocationProviderManager(GPS_PROVIDER);
+ if (gpsManager == null) {
+ return null;
+ }
+
+ Location location = gpsManager.getLastLocationUnsafe(UserHandle.USER_ALL,
+ PERMISSION_FINE, false, Long.MAX_VALUE);
+ if (location == null) {
+ return null;
+ }
+
+ long currentNanos = SystemClock.elapsedRealtimeNanos();
+ long deltaMs = NANOSECONDS.toMillis(
+ location.getElapsedRealtimeAgeNanos(currentNanos));
+ return new LocationTime(location.getTime() + deltaMs, currentNanos);
+ }
}
private static class SystemInjector implements Injector {
diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java
index 0fe66e0..f0dd8b5 100644
--- a/services/core/java/com/android/server/location/LocationShellCommand.java
+++ b/services/core/java/com/android/server/location/LocationShellCommand.java
@@ -49,6 +49,12 @@
}
switch (cmd) {
+ case "is-location-enabled": {
+ int userId = parseUserId();
+ boolean enabled = mService.isLocationEnabledForUser(userId);
+ getOutPrintWriter().println(enabled);
+ return 0;
+ }
case "set-location-enabled": {
int userId = parseUserId();
boolean enabled = Boolean.parseBoolean(getNextArgRequired());
@@ -238,6 +244,8 @@
pw.println("Location service commands:");
pw.println(" help or -h");
pw.println(" Print this help text.");
+ pw.println(" is-location-enabled [--user <USER_ID>]");
+ pw.println(" Gets the master location switch enabled state.");
pw.println(" set-location-enabled [--user <USER_ID>] true|false");
pw.println(" Sets the master location switch enabled state.");
pw.println(" providers");
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 63a42f8..dc1a26a 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
+import android.hardware.SensorPrivacyManager;
import android.hardware.contexthub.V1_0.AsyncEventType;
import android.hardware.contexthub.V1_0.ContextHub;
import android.hardware.contexthub.V1_0.ContextHubMsg;
@@ -117,6 +118,9 @@
// True if WiFi is available for the Context Hub
private boolean mIsWifiAvailable = false;
+ // True if audio is disabled for the ContextHub
+ private boolean mIsAudioDisabled = false;
+
// Lock object for sendWifiSettingUpdate()
private final Object mSendWifiSettingUpdateLock = new Object();
@@ -256,6 +260,21 @@
}
}, UserHandle.USER_ALL);
}
+
+ if (mContextHubWrapper.supportsMicrophoneDisableSettingNotifications()) {
+ sendMicrophoneDisableSettingUpdate();
+
+ SensorPrivacyManager.OnSensorPrivacyChangedListener listener =
+ new SensorPrivacyManager.OnSensorPrivacyChangedListener() {
+ @Override
+ public void onSensorPrivacyChanged(boolean enabled) {
+ sendMicrophoneDisableSettingUpdate();
+ }
+ };
+ SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext);
+ manager.addSensorPrivacyListener(
+ SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE, listener);
+ }
}
/**
@@ -999,6 +1018,21 @@
mContextHubWrapper.onAirplaneModeSettingChanged(enabled);
}
+ /**
+ * Obtains the latest microphone disable setting value and notifies the
+ * Context Hub.
+ */
+ private void sendMicrophoneDisableSettingUpdate() {
+ SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext);
+ boolean disabled = manager.isIndividualSensorPrivacyEnabled(
+ SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE);
+ if (mIsAudioDisabled != disabled) {
+ mIsAudioDisabled = disabled;
+ mContextHubWrapper.onMicrophoneDisableSettingChanged(disabled);
+ }
+ }
+
+
private String getCallingPackageName() {
return mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
}
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 4242d72..c11e289 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -129,6 +129,18 @@
*/
public abstract void onAirplaneModeSettingChanged(boolean enabled);
+ /**
+ * @return True if this version of the Contexthub HAL supports microphone
+ * disable setting notifications.
+ */
+ public abstract boolean supportsMicrophoneDisableSettingNotifications();
+
+ /**
+ * Notifies the Contexthub implementation of a microphone disable setting
+ * change.
+ */
+ public abstract void onMicrophoneDisableSettingChanged(boolean enabled);
+
private static class ContextHubWrapperV1_0 extends IContextHubWrapper {
private android.hardware.contexthub.V1_0.IContexthub mHub;
@@ -152,6 +164,10 @@
return false;
}
+ public boolean supportsMicrophoneDisableSettingNotifications() {
+ return false;
+ }
+
public void onLocationSettingChanged(boolean enabled) {
}
@@ -160,6 +176,9 @@
public void onAirplaneModeSettingChanged(boolean enabled) {
}
+
+ public void onMicrophoneDisableSettingChanged(boolean enabled) {
+ }
}
private static class ContextHubWrapperV1_1 extends IContextHubWrapper {
@@ -185,6 +204,10 @@
return false;
}
+ public boolean supportsMicrophoneDisableSettingNotifications() {
+ return false;
+ }
+
public void onLocationSettingChanged(boolean enabled) {
try {
mHub.onSettingChanged(Setting.LOCATION,
@@ -199,6 +222,9 @@
public void onAirplaneModeSettingChanged(boolean enabled) {
}
+
+ public void onMicrophoneDisableSettingChanged(boolean enabled) {
+ }
}
private static class ContextHubWrapperV1_2 extends IContextHubWrapper {
@@ -224,6 +250,10 @@
return true;
}
+ public boolean supportsMicrophoneDisableSettingNotifications() {
+ return true;
+ }
+
public void onLocationSettingChanged(boolean enabled) {
sendSettingChanged(Setting.LOCATION,
enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
@@ -239,6 +269,11 @@
enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
}
+ public void onMicrophoneDisableSettingChanged(boolean enabled) {
+ sendSettingChanged(android.hardware.contexthub.V1_2.Setting.GLOBAL_MIC_DISABLE,
+ enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
+ }
+
private void sendSettingChanged(byte setting, byte newValue) {
try {
mHub.onSettingChanged_1_2(setting, newValue);
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 92957aa..b6695c2 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -193,7 +193,9 @@
IGnssMeasurementsListener listener, String packageName,
@Nullable String attributionTag) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
-
+ if (request.isCorrelationVectorOutputsEnabled()) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
+ }
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
mGnssMeasurementsProvider.addListener(request, identity, listener);
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index b623e27..305bc9b 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -118,7 +118,8 @@
@Override
protected boolean registerWithService(GnssMeasurementRequest request,
Collection<GnssListenerRegistration> registrations) {
- if (mGnssNative.startMeasurementCollection(request.isFullTracking())) {
+ if (mGnssNative.startMeasurementCollection(request.isFullTracking(),
+ request.isCorrelationVectorOutputsEnabled())) {
if (D) {
Log.d(TAG, "starting gnss measurements (" + request + ")");
}
@@ -160,18 +161,26 @@
protected GnssMeasurementRequest mergeRegistrations(
Collection<GnssListenerRegistration> registrations) {
boolean fullTracking = false;
+ boolean enableCorrVecOutputs = false;
+
if (mSettingsHelper.isGnssMeasurementsFullTrackingEnabled()) {
fullTracking = true;
- } else {
- for (GnssListenerRegistration registration : registrations) {
- if (registration.getRequest().isFullTracking()) {
- fullTracking = true;
- break;
- }
+ }
+
+ for (GnssListenerRegistration registration : registrations) {
+ GnssMeasurementRequest request = registration.getRequest();
+ if (request.isFullTracking()) {
+ fullTracking = true;
+ }
+ if (request.isCorrelationVectorOutputsEnabled()) {
+ enableCorrVecOutputs = true;
}
}
- return new GnssMeasurementRequest.Builder().setFullTracking(fullTracking).build();
+ return new GnssMeasurementRequest.Builder()
+ .setFullTracking(fullTracking)
+ .setCorrelationVectorOutputsEnabled(enableCorrVecOutputs)
+ .build();
}
@Override
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index 402e84b..7e2f089 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -733,9 +733,10 @@
/**
* Starts measurement collection.
*/
- public boolean startMeasurementCollection(boolean enableFullTracking) {
+ public boolean startMeasurementCollection(boolean enableFullTracking,
+ boolean enableCorrVecOutputs) {
Preconditions.checkState(mRegistered);
- return mGnssHal.startMeasurementCollection(enableFullTracking);
+ return mGnssHal.startMeasurementCollection(enableFullTracking, enableCorrVecOutputs);
}
/**
@@ -1274,8 +1275,9 @@
return native_is_measurement_supported();
}
- protected boolean startMeasurementCollection(boolean enableFullTracking) {
- return native_start_measurement_collection(enableFullTracking);
+ protected boolean startMeasurementCollection(boolean enableFullTracking,
+ boolean enableCorrVecOutputs) {
+ return native_start_measurement_collection(enableFullTracking, enableCorrVecOutputs);
}
protected boolean stopMeasurementCollection() {
@@ -1438,7 +1440,8 @@
private static native boolean native_is_measurement_supported();
- private static native boolean native_start_measurement_collection(boolean enableFullTracking);
+ private static native boolean native_start_measurement_collection(boolean enableFullTracking,
+ boolean enableCorrVecOutputs);
private static native boolean native_stop_measurement_collection();
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 14f0100..b06389a 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -18,7 +18,6 @@
import static android.app.compat.CompatChanges.isChangeEnabled;
import static android.location.LocationManager.DELIVER_HISTORICAL_LOCATIONS;
-import static android.location.LocationManager.FUSED_PROVIDER;
import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationManager.KEY_FLUSH_COMPLETE;
import static android.location.LocationManager.KEY_LOCATION_CHANGED;
@@ -43,6 +42,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.AlarmManager.OnAlarmListener;
+import android.app.BroadcastOptions;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -86,7 +86,6 @@
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.LocalServices;
-import com.android.server.PendingIntentUtils;
import com.android.server.location.LocationPermissions;
import com.android.server.location.LocationPermissions.PermissionLevel;
import com.android.server.location.fudger.LocationFudger;
@@ -131,6 +130,9 @@
private static final String WAKELOCK_TAG = "*location*";
private static final long WAKELOCK_TIMEOUT_MS = 30 * 1000;
+ // duration PI location clients are put on the allowlist to start a fg service
+ private static final long TEMPORARY_APP_ALLOWLIST_DURATION_MS = 10 * 1000;
+
// fastest interval at which clients may receive coarse locations
private static final long MIN_COARSE_INTERVAL_MS = 10 * 60 * 1000;
@@ -215,6 +217,11 @@
public void deliverOnLocationChanged(LocationResult locationResult,
@Nullable Runnable onCompleteCallback)
throws PendingIntent.CanceledException {
+ BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setDontSendToRestrictedApps(true);
+ // allows apps to start a fg service in response to a location PI
+ options.setTemporaryAppWhitelistDuration(TEMPORARY_APP_ALLOWLIST_DURATION_MS);
+
mPendingIntent.send(
mContext,
0,
@@ -225,22 +232,26 @@
: null,
null,
null,
- PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
+ options.toBundle());
}
@Override
public void deliverOnFlushComplete(int requestCode) throws PendingIntent.CanceledException {
+ BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setDontSendToRestrictedApps(true);
+
mPendingIntent.send(mContext, 0, new Intent().putExtra(KEY_FLUSH_COMPLETE, requestCode),
- null, null, null,
- PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
+ null, null, null, options.toBundle());
}
@Override
public void deliverOnProviderEnabledChanged(String provider, boolean enabled)
throws PendingIntent.CanceledException {
+ BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setDontSendToRestrictedApps(true);
+
mPendingIntent.send(mContext, 0, new Intent().putExtra(KEY_PROVIDER_ENABLED, enabled),
- null, null, null,
- PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
+ null, null, null, options.toBundle());
}
}
@@ -2332,8 +2343,8 @@
// do not send change notifications if we just saw this user for the first time
if (wasEnabled != null) {
- // fused and passive provider never get public updates for legacy reasons
- if (!FUSED_PROVIDER.equals(mName) && !PASSIVE_PROVIDER.equals(mName)) {
+ // passive provider never get public updates for legacy reasons
+ if (!PASSIVE_PROVIDER.equals(mName)) {
Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION)
.putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName)
.putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, enabled)
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 5d0544b..7dd961a 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -753,8 +753,9 @@
pw.increaseIndent();
File[] files = userPath.listFiles();
if (files != null) {
+ Arrays.sort(files);
for (File file : files) {
- pw.println(String.format("%4d %s %s", file.length(),
+ pw.println(String.format("%6d %s %s", file.length(),
LockSettingsService.timestampToString(file.lastModified()),
file.getName()));
}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 289290b..fbec915 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -224,6 +224,10 @@
for (UserInfo user : rebootEscrowUsers) {
allUsersUnlocked &= restoreRebootEscrowForUser(user.id, escrowKey, kk);
}
+
+ // Clear the old key in keystore. A new key will be generated by new RoR requests.
+ mKeyStoreManager.clearKeyStoreEncryptionKey();
+
onEscrowRestoreComplete(allUsersUnlocked);
}
@@ -273,9 +277,6 @@
} catch (IOException e) {
Slog.w(TAG, "Could not load reboot escrow data for user " + userId, e);
return false;
- } finally {
- // Clear the old key in keystore. A new key will be generated by new RoR requests.
- mKeyStoreManager.clearKeyStoreEncryptionKey();
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 1b27ef4..c0381e4 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -109,6 +109,10 @@
private static final int LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout()
+ /* Buffer for delayed delivery of key event */ 50;
private static final int MULTI_TAP_TIMEOUT = ViewConfiguration.getMultiPressTimeout();
+ /**
+ * Copied from Settings.System.MEDIA_BUTTON_RECEIVER
+ */
+ private static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver";
private final Context mContext;
private final SessionManagerImpl mSessionManagerImpl;
@@ -131,7 +135,6 @@
private KeyguardManager mKeyguardManager;
private AudioManager mAudioManager;
- private ContentResolver mContentResolver;
private boolean mHasFeatureLeanback;
// The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
@@ -183,7 +186,6 @@
}
}
}, null /* handler */);
- mContentResolver = mContext.getContentResolver();
mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_LEANBACK);
@@ -831,6 +833,7 @@
*/
final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
private final int mFullUserId;
+ private final ContentResolver mContentResolver;
private final MediaSessionStack mPriorityStack;
private final HashMap<IBinder, OnMediaKeyEventDispatchedListenerRecord>
mOnMediaKeyEventDispatchedListeners = new HashMap<>();
@@ -848,10 +851,12 @@
FullUserRecord(int fullUserId) {
mFullUserId = fullUserId;
+ mContentResolver = mContext.createContextAsUser(UserHandle.of(mFullUserId), 0)
+ .getContentResolver();
mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this);
// Restore the remembered media button receiver before the boot.
- String mediaButtonReceiverInfo = Settings.Secure.getStringForUser(mContentResolver,
- Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
+ String mediaButtonReceiverInfo = Settings.Secure.getString(mContentResolver,
+ MEDIA_BUTTON_RECEIVER);
mLastMediaButtonReceiverHolder =
MediaButtonReceiverHolder.unflattenFromString(
mContext, mediaButtonReceiverInfo);
@@ -970,10 +975,9 @@
mLastMediaButtonReceiverHolder = sessionRecord.getMediaButtonReceiver();
String mediaButtonReceiverInfo = (mLastMediaButtonReceiverHolder == null)
? "" : mLastMediaButtonReceiverHolder.flattenToString();
- Settings.Secure.putStringForUser(mContentResolver,
- Settings.System.MEDIA_BUTTON_RECEIVER,
- mediaButtonReceiverInfo,
- mFullUserId);
+ Settings.Secure.putString(mContentResolver,
+ MEDIA_BUTTON_RECEIVER,
+ mediaButtonReceiverInfo);
}
private void pushAddressedPlayerChangedLocked(
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 9c68349..5fa7998 100644
--- a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java
+++ b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java
@@ -18,8 +18,15 @@
import android.content.Context;
import android.media.metrics.IPlaybackMetricsManager;
+import android.media.metrics.NetworkEvent;
+import android.media.metrics.PlaybackErrorEvent;
import android.media.metrics.PlaybackMetrics;
+import android.media.metrics.PlaybackStateEvent;
+import android.media.metrics.TrackChangeEvent;
+import android.os.Binder;
import android.util.Base64;
+import android.util.StatsEvent;
+import android.util.StatsLog;
import com.android.server.SystemService;
@@ -50,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
}
@@ -60,5 +94,57 @@
String id = Base64.encodeToString(byteId, Base64.DEFAULT);
return id;
}
+
+ @Override
+ public void reportPlaybackErrorEvent(
+ String sessionId, PlaybackErrorEvent event, int userId) {
+ StatsEvent statsEvent = StatsEvent.newBuilder()
+ .setAtomId(323)
+ .writeString(sessionId)
+ .writeString(event.getExceptionStack())
+ .writeInt(event.getErrorCode())
+ .writeInt(event.getSubErrorCode())
+ .writeLong(event.getTimeSincePlaybackCreatedMillis())
+ .usePooledBuffer()
+ .build();
+ StatsLog.write(statsEvent);
+ }
+
+ public void reportNetworkEvent(
+ String sessionId, NetworkEvent event, int userId) {
+ StatsEvent statsEvent = StatsEvent.newBuilder()
+ .setAtomId(321)
+ .writeString(sessionId)
+ .writeInt(event.getType())
+ .writeLong(event.getTimeSincePlaybackCreatedMillis())
+ .usePooledBuffer()
+ .build();
+ StatsLog.write(statsEvent);
+ }
+
+ @Override
+ public void reportTrackChangeEvent(
+ String sessionId, TrackChangeEvent event, int userId) {
+ StatsEvent statsEvent = StatsEvent.newBuilder()
+ .setAtomId(321)
+ .writeString(sessionId)
+ .writeInt(event.getTrackState())
+ .writeInt(event.getTrackChangeReason())
+ .writeString(event.getContainerMimeType())
+ .writeString(event.getSampleMimeType())
+ .writeString(event.getCodecName())
+ .writeInt(event.getBitrate())
+ .writeLong(event.getTimeSincePlaybackCreatedMillis())
+ .writeInt(event.getTrackType())
+ .writeString(event.getLanguage())
+ .writeString(event.getLanguageRegion())
+ .writeInt(event.getChannelCount())
+ .writeInt(event.getSampleRate())
+ .writeInt(event.getWidth())
+ .writeInt(event.getHeight())
+ .usePooledBuffer()
+ .build();
+ StatsLog.write(statsEvent);
+ }
}
}
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 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/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ab5238f..61b218c 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -128,6 +128,7 @@
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AutomaticZenRule;
+import android.app.BroadcastOptions;
import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.ITransientNotification;
@@ -5960,7 +5961,9 @@
PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
if (pendingIntent != null) {
am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
- ALLOWLIST_TOKEN, duration);
+ ALLOWLIST_TOKEN, duration,
+ BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED
+ );
am.setPendingIntentAllowBgActivityStarts(pendingIntent.getTarget(),
ALLOWLIST_TOKEN, (FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER
| FLAG_SERVICE_SENDER));
@@ -6078,7 +6081,6 @@
if (!isAppForeground && metadata != null) {
int flags = metadata.getFlags();
flags &= ~Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE;
- flags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
metadata.setFlags(flags);
}
}
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 dd507a3..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,10 +93,11 @@
}
@Override
- @RequiresPermission(android.Manifest.permission.DUMP)
- public void cancelBugreport() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
- "cancelBugreport");
+ @RequiresPermission(android.Manifest.permission.DUMP) // or carrier privileges
+ public void cancelBugreport(int callingUidUnused, String callingPackage) {
+ int callingUid = Binder.getCallingUid();
+ enforcePermission(callingPackage, callingUid, true /* checkCarrierPrivileges */);
+
synchronized (mLock) {
IDumpstate ds = getDumpstateBinderServiceLocked();
if (ds == null) {
@@ -104,7 +105,11 @@
return;
}
try {
- ds.cancelBugreport();
+ // Note: this may throw SecurityException back out to the caller if they aren't
+ // allowed to cancel the report, in which case we should NOT be setting ctl.stop,
+ // since that would unintentionally kill some other app's bugreport, which we
+ // specifically disallow.
+ ds.cancelBugreport(callingUid, callingPackage);
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException in cancelBugreport", e);
}
@@ -127,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.
*
@@ -182,7 +215,7 @@
// lifecycle correctly. If we don't subsequent callers will get
// BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS error.
// Note that listener will be notified by the death recipient below.
- cancelBugreport();
+ cancelBugreport(callingUid, callingPackage);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index f4472b4..2b5c393 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -30,7 +30,6 @@
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
import static android.content.pm.PackageManager.INSTALL_STAGED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
-import static android.content.pm.PackageParser.APEX_FILE_EXTENSION;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_WRONLY;
@@ -72,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;
@@ -187,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";
@@ -251,6 +252,7 @@
private static final ApkChecksum[] EMPTY_FILE_CHECKSUM_ARRAY = {};
private static final String SYSTEM_DATA_LOADER_PACKAGE = "android";
+ private static final String APEX_FILE_EXTENSION = ".apex";
private static final int INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS = 2000;
private static final int INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS = 7000;
@@ -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")
@@ -331,8 +335,6 @@
private boolean mCommitted = false;
@GuardedBy("mLock")
private boolean mRelinquished = false;
- @GuardedBy("mLock")
- private boolean mDestroyed = false;
/** Permissions have been accepted by the user (see {@link #setPermissionsResult}) */
@GuardedBy("mLock")
@@ -399,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;
@@ -762,8 +782,13 @@
@GuardedBy("mLock")
private boolean mVerityFound;
- @GuardedBy("mLock")
- private boolean mDataLoaderFinished = false;
+ /**
+ * Both flags should be guarded with mLock whenever changes need to be in lockstep.
+ * Ok to check without mLock in case the proper check is done later, e.g. status callbacks
+ * for DataLoaders with deferred processing.
+ */
+ private volatile boolean mDestroyed = false;
+ private volatile boolean mDataLoaderFinished = false;
@GuardedBy("mLock")
private IncrementalFileStorages mIncrementalFileStorages;
@@ -918,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,
@@ -964,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)) {
@@ -1179,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
@@ -1250,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;
}
@@ -1266,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");
@@ -1274,7 +1312,7 @@
throw new IllegalStateException("Duplicate checksums.");
}
- mChecksums.put(name, checksums);
+ mChecksums.put(name, new PerFileChecksum(checksums, signature));
}
}
@@ -3029,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;
}
@@ -3045,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);
@@ -3060,8 +3118,6 @@
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
"Failed to store digests for " + mPackageName, e);
}
-
- stageFileLocked(targetDigestsFile, targetDigestsFile);
}
@GuardedBy("mLock")
@@ -3562,19 +3618,15 @@
return;
}
- synchronized (mLock) {
- if (mDestroyed || mDataLoaderFinished) {
- // No need to worry about post installation
- return;
- }
+ if (mDestroyed || mDataLoaderFinished) {
+ // No need to worry about post installation
+ return;
}
try {
IDataLoader dataLoader = dataLoaderManager.getDataLoader(dataLoaderId);
if (dataLoader == null) {
- synchronized (mLock) {
- mDataLoaderFinished = true;
- }
+ mDataLoaderFinished = true;
dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"Failure to obtain data loader");
return;
@@ -3607,9 +3659,7 @@
break;
}
case IDataLoaderStatusListener.DATA_LOADER_IMAGE_READY: {
- synchronized (mLock) {
- mDataLoaderFinished = true;
- }
+ mDataLoaderFinished = true;
if (hasParentSessionId()) {
mSessionProvider.getSession(
getParentSessionId()).dispatchSessionSealed();
@@ -3622,9 +3672,7 @@
break;
}
case IDataLoaderStatusListener.DATA_LOADER_IMAGE_NOT_READY: {
- synchronized (mLock) {
- mDataLoaderFinished = true;
- }
+ mDataLoaderFinished = true;
dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"Failed to prepare image.");
if (manualStartAndDestroy) {
@@ -3643,9 +3691,7 @@
break;
}
case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
- synchronized (mLock) {
- mDataLoaderFinished = true;
- }
+ mDataLoaderFinished = true;
dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"DataLoader reported unrecoverable failure.");
break;
@@ -3683,11 +3729,9 @@
final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() {
@Override
public void onHealthStatus(int storageId, int status) {
- synchronized (mLock) {
- if (mDestroyed || mDataLoaderFinished) {
- // No need to worry about post installation
- return;
- }
+ if (mDestroyed || mDataLoaderFinished) {
+ // No need to worry about post installation
+ return;
}
switch (status) {
@@ -3702,9 +3746,7 @@
// fallthrough
case IStorageHealthListener.HEALTH_STATUS_UNHEALTHY:
// Even ADB installation can't wait for missing pages for too long.
- synchronized (mLock) {
- mDataLoaderFinished = true;
- }
+ mDataLoaderFinished = true;
dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"Image is missing pages required for installation.");
break;
@@ -3715,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(),
@@ -4288,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);
@@ -4297,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);
@@ -4412,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
@@ -4454,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) {
@@ -4482,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/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e20ed05..19a94b3 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -4762,13 +4762,32 @@
final boolean hasParent = user.profileGroupId != user.id
&& user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID;
if (verbose) {
- pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s%s%s\n", i, user.id, user.name,
+ final DevicePolicyManagerInternal dpm = getDevicePolicyManagerInternal();
+ String deviceOwner = "";
+ String profileOwner = "";
+ if (dpm != null) {
+ final long ident = Binder.clearCallingIdentity();
+ // NOTE: dpm methods below CANNOT be called while holding the mUsersLock
+ try {
+ if (dpm.getDeviceOwnerUserId() == user.id) {
+ deviceOwner = " (device-owner)";
+ }
+ if (dpm.getProfileOwnerAsUser(user.id) != null) {
+ profileOwner = " (profile-owner)";
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s%s%s%s%s\n", i, user.id,
+ user.name,
UserInfo.flagsToString(user.flags),
hasParent ? " (parentId=" + user.profileGroupId + ")" : "",
running ? " (running)" : "",
user.partial ? " (partial)" : "",
user.preCreated ? " (pre-created)" : "",
user.convertedFromPreCreated ? " (converted)" : "",
+ deviceOwner, profileOwner,
current ? " (current)" : "");
} else {
// NOTE: the standard "list users" command is used by integration tests and
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index aa2e1ff..607c165 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
+import static android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.AppOpsManager.OP_TOAST_WINDOW;
import static android.content.Context.CONTEXT_RESTRICTED;
@@ -38,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;
@@ -489,6 +491,7 @@
boolean mWakeOnDpadKeyPress;
boolean mWakeOnAssistKeyPress;
boolean mWakeOnBackKeyPress;
+ long mWakeUpToLastStateTimeout;
private boolean mHandleVolumeKeysInWM;
@@ -1845,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);
@@ -2191,6 +2197,11 @@
== PERMISSION_GRANTED) ? ADD_OKAY : ADD_PERMISSION_DENIED;
}
+ if (mContext.checkCallingOrSelfPermission(SYSTEM_APPLICATION_OVERLAY)
+ == PERMISSION_GRANTED) {
+ return ADD_OKAY;
+ }
+
// check if user has enabled this operation. SecurityException will be thrown if this app
// has not been allowed by the user. The reason to use "noteOp" (instead of checkOp) is to
// make sure the usage is logged.
@@ -2594,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);
@@ -3550,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;
}
@@ -3873,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) {
@@ -4313,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,
@@ -4998,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) {}
@@ -5026,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 ebfffec..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;
@@ -40,6 +41,9 @@
void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
+ /** Collapses the notification shade. */
+ void collapsePanels();
+
void dismissKeyboardShortcutsMenu();
void toggleKeyboardShortcutsMenu(int deviceId);
@@ -81,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);
@@ -125,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 c085009..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);
}
@@ -384,6 +380,16 @@
}
@Override
+ public void collapsePanels() {
+ if (mBar != null) {
+ try {
+ mBar.animateCollapsePanels();
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ @Override
public void dismissKeyboardShortcutsMenu() {
if (mBar != null) {
try {
@@ -511,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) { }
}
}
@@ -971,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,
@@ -1058,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;
@@ -1067,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) {
@@ -1100,14 +1073,6 @@
}
}
- private void setFullscreen(boolean isFullscreen) {
- mFullscreen = isFullscreen;
- }
-
- private void setImmersive(boolean isImmersive) {
- mImmersive = isImmersive;
- }
-
private int getDisabled1() {
return mDisabled1;
}
@@ -1190,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/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index d0c6323..2ead3be 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -27,7 +27,6 @@
import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.content.Context;
-import android.location.LocationManager;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -35,7 +34,6 @@
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.SystemProperties;
-import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -335,19 +333,6 @@
return isGeoLocationTimeZoneDetectionEnabled(mContext);
}
- boolean isLocationEnabled(@UserIdInt int userId) {
- enforceManageTimeZoneDetectorPermission();
-
- final long token = mCallerIdentityInjector.clearCallingIdentity();
- try {
- UserHandle user = UserHandle.of(userId);
- LocationManager locationManager = mContext.getSystemService(LocationManager.class);
- return locationManager.isLocationEnabledForUser(user);
- } finally {
- mCallerIdentityInjector.restoreCallingIdentity(token);
- }
- }
-
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
@Nullable String[] args) {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
index e965f55..8c529c4 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
@@ -18,7 +18,6 @@
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_GEO_DETECTION_ENABLED;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_GEO_DETECTION_SUPPORTED;
-import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_LOCATION_ENABLED;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SET_AUTO_DETECTION_ENABLED;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SET_GEO_DETECTION_ENABLED;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE;
@@ -57,8 +56,6 @@
return runSetAutoDetectionEnabled();
case SHELL_COMMAND_IS_GEO_DETECTION_SUPPORTED:
return runIsGeoDetectionSupported();
- case SHELL_COMMAND_IS_LOCATION_ENABLED:
- return runIsLocationEnabled();
case SHELL_COMMAND_IS_GEO_DETECTION_ENABLED:
return runIsGeoDetectionEnabled();
case SHELL_COMMAND_SET_GEO_DETECTION_ENABLED:
@@ -92,14 +89,6 @@
return 0;
}
- private int runIsLocationEnabled() {
- final PrintWriter pw = getOutPrintWriter();
- int userId = UserHandle.USER_CURRENT;
- boolean enabled = mInterface.isLocationEnabled(userId);
- pw.println(enabled);
- return 0;
- }
-
private int runIsGeoDetectionEnabled() {
final PrintWriter pw = getOutPrintWriter();
int userId = UserHandle.USER_CURRENT;
@@ -176,9 +165,6 @@
pw.printf(" %s\n", SHELL_COMMAND_IS_GEO_DETECTION_SUPPORTED);
pw.println(" Prints true/false according to whether geolocation time zone detection is"
+ " supported on this device");
- pw.printf(" %s\n", SHELL_COMMAND_IS_LOCATION_ENABLED);
- pw.println(" Prints true/false according to whether the master location toggle is"
- + " enabled for the current user");
pw.printf(" %s\n", SHELL_COMMAND_IS_GEO_DETECTION_ENABLED);
pw.println(" Prints true/false according to the geolocation tz detection setting");
pw.printf(" %s true|false\n", SHELL_COMMAND_SET_GEO_DETECTION_ENABLED);
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/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index ff5b65b..a1d2f8a 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -1874,6 +1874,46 @@
}
@Override
+ public void pauseRecording(IBinder sessionToken, @NonNull Bundle params, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "pauseRecording");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId)
+ .pauseRecording(params);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slog.e(TAG, "error in pauseRecording", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void resumeRecording(IBinder sessionToken, @NonNull Bundle params, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "resumeRecording");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId)
+ .resumeRecording(params);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slog.e(TAG, "error in resumeRecording", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
!= PackageManager.PERMISSION_GRANTED) {
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/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index a34a507..536375f 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -36,6 +36,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import java.util.ArrayList;
@@ -59,7 +60,8 @@
private final Vibrator mVibrator;
private final AudioManager mAudioManager;
private final SettingsObserver mSettingObserver;
- private final UidObserver mUidObserver;
+ @VisibleForTesting
+ final UidObserver mUidObserver;
@GuardedBy("mLock")
private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>();
@@ -251,8 +253,7 @@
* allowed to play in the background (i.e. it's a notification, ringtone or alarm vibration).
*/
public boolean shouldVibrateForUid(int uid, int usageHint) {
- return mUidObserver.isUidForeground(uid) || isNotification(usageHint)
- || isRingtone(usageHint) || isAlarm(usageHint);
+ return mUidObserver.isUidForeground(uid) || isClassAlarm(usageHint);
}
/**
@@ -292,6 +293,11 @@
return usageHint == VibrationAttributes.USAGE_ALARM;
}
+ private static boolean isClassAlarm(int usageHint) {
+ return (usageHint & VibrationAttributes.USAGE_CLASS_MASK)
+ == VibrationAttributes.USAGE_CLASS_ALARM;
+ }
+
/** Updates all vibration settings and triggers registered listeners. */
public void updateSettings() {
synchronized (mLock) {
@@ -414,7 +420,8 @@
}
/** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */
- private final class UidObserver extends IUidObserver.Stub {
+ @VisibleForTesting
+ final class UidObserver extends IUidObserver.Stub {
private final SparseArray<Integer> mProcStatesCache = new SparseArray<>();
public boolean isUidForeground(int uid) {
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
new file mode 100644
index 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/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 03cf021..6bca484 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -288,7 +288,7 @@
return;
}
mLastLaunchedActivity = r;
- if (!r.noDisplay) {
+ if (!r.noDisplay && !r.isReportedDrawn()) {
if (DEBUG_METRICS) Slog.i(TAG, "Add pending draw " + r);
mPendingDrawActivities.add(r);
}
@@ -576,7 +576,7 @@
+ " processSwitch=" + processSwitch + " info=" + info);
}
- if (launchedActivity.isReportedDrawn()) {
+ if (launchedActivity.isReportedDrawn() && launchedActivity.isVisible()) {
// Launched activity is already visible. We cannot measure windows drawn delay.
abort(info, "launched activity already visible");
return;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a9c5474..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;
}
/**
@@ -6569,7 +6588,12 @@
}
return !isResizeable() && (info.isFixedOrientation() || info.hasFixedAspectRatio())
// The configuration of non-standard type should be enforced by system.
- && isActivityTypeStandard()
+ // {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} is set when this activity is
+ // added to a task, but this function is called when resolving the launch params, at
+ // which point, the activity type is still undefined if it will be standard.
+ // For other non-standard types, the type is set in the constructor, so this should
+ // not be a problem.
+ && isActivityTypeStandardOrUndefined()
&& !mAtmService.mForceResizableActivities;
}
@@ -7798,13 +7822,16 @@
/**
* The precomputed insets of the display in each rotation. This is used to make the size
* compatibility mode activity compute the configuration without relying on its current display.
- * This currently only supports fullscreen and freeform windowing mode.
*/
static class CompatDisplayInsets {
+ /** The container width on rotation 0. */
private final int mWidth;
+ /** The container height on rotation 0. */
private final int mHeight;
+ /** Whether the {@link Task} windowingMode represents a floating window*/
final boolean mIsFloating;
-
+ /** Whether the {@link Task} is letterboxed when the unresizable activity is first shown. */
+ final boolean mIsTaskLetterboxed;
/**
* The nonDecorInsets for each rotation. Includes the navigation bar and cutout insets. It
* is used to compute the appBounds.
@@ -7831,27 +7858,24 @@
mNonDecorInsets[rotation] = emptyRect;
mStableInsets[rotation] = emptyRect;
}
+ mIsTaskLetterboxed = false;
return;
}
final Task task = container.getTask();
- if (task != null && task.isTaskLetterboxed()) {
- // For apps in Task letterbox, it should fill the task bounds.
- final Point dimensions = getRotationZeroDimensions(task);
- mWidth = dimensions.x;
- mHeight = dimensions.y;
- } else {
- // If the activity is not floating nor letterboxed, assume it fills the root.
- final RootDisplayArea root = container.getRootDisplayArea();
- if (root == null || root == display) {
- mWidth = display.mBaseDisplayWidth;
- mHeight = display.mBaseDisplayHeight;
- } else {
- final Point dimensions = getRotationZeroDimensions(root);
- mWidth = dimensions.x;
- mHeight = dimensions.y;
- }
- }
+ mIsTaskLetterboxed = task != null && task.isTaskLetterboxed();
+
+ // Store the bounds of the Task for the non-resizable activity to use in size compat
+ // mode so that the activity will not be resized regardless the windowing mode it is
+ // currently in.
+ final WindowContainer filledContainer = task != null ? task : display;
+ final Point dimensions = getRotationZeroDimensions(filledContainer);
+ mWidth = dimensions.x;
+ mHeight = dimensions.y;
+
+ // Bounds of the filled container if it doesn't fill the display.
+ final Rect unfilledContainerBounds =
+ filledContainer.getBounds().equals(display.getBounds()) ? null : new Rect();
final DisplayPolicy policy = display.getDisplayPolicy();
for (int rotation = 0; rotation < 4; rotation++) {
mNonDecorInsets[rotation] = new Rect();
@@ -7864,6 +7888,20 @@
policy.getNonDecorInsetsLw(rotation, dw, dh, cutout, mNonDecorInsets[rotation]);
mStableInsets[rotation].set(mNonDecorInsets[rotation]);
policy.convertNonDecorInsetsToStableInsets(mStableInsets[rotation], rotation);
+
+ if (unfilledContainerBounds == null) {
+ continue;
+ }
+ // The insets is based on the display, but the container may be smaller than the
+ // display, so update the insets to exclude parts that are not intersected with the
+ // container.
+ unfilledContainerBounds.set(filledContainer.getBounds());
+ display.rotateBounds(
+ filledContainer.getConfiguration().windowConfiguration.getRotation(),
+ rotation,
+ unfilledContainerBounds);
+ updateInsetsForBounds(unfilledContainerBounds, dw, dh, mNonDecorInsets[rotation]);
+ updateInsetsForBounds(unfilledContainerBounds, dw, dh, mStableInsets[rotation]);
}
}
@@ -7881,6 +7919,18 @@
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 7a4bcb1..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;
@@ -118,6 +119,7 @@
import com.android.server.am.PendingIntentRecord;
import com.android.server.pm.InstantAppResolver;
import com.android.server.power.ShutdownCheckPoints;
+import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.NeededUriGrants;
import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
import com.android.server.wm.ActivityTaskSupervisor.PendingActivityLaunch;
@@ -1572,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();
@@ -1589,6 +1595,20 @@
newTransition.abort();
}
} else {
+ if (!mAvoidMoveToFront && mDoResume
+ && mRootWindowContainer.hasVisibleWindowAboveNotificationShade(
+ r.launchedFromUid)) {
+ // If the UID launching the activity has a visible window on top of the
+ // notification shade and it's launching an activity that's going to be at the
+ // front, we should move the shade out of the way so the user can see it.
+ // We want to avoid the case where the activity is launched on top of a
+ // background task which is not moved to the front.
+ StatusBarManagerInternal statusBar = mService.getStatusBarManagerInternal();
+ if (statusBar != null) {
+ // This results in a async call since the interface is one-way
+ statusBar.collapsePanels();
+ }
+ }
if (result == START_SUCCESS || result == START_TASK_TO_FRONT) {
// The activity is started new rather than just brought forward, so record
// it as an existence change.
@@ -1596,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/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 166663a..ecdef3f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -385,15 +385,6 @@
}
}
- /**
- * Set the corresponding display information for the process global configuration. To be called
- * when we need to show IME on a different display.
- *
- * @param pid The process id associated with the IME window.
- * @param displayId The ID of the display showing the IME.
- */
- public abstract void onImeWindowSetOnDisplay(int pid, int displayId);
-
public abstract void sendActivityResult(int callingUid, IBinder activityToken,
String resultWho, int requestCode, int resultCode, Intent data);
public abstract void clearPendingResultForActivity(
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index caf20a9..b332739 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -28,6 +28,8 @@
import static android.Manifest.permission.REMOVE_TASKS;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.Manifest.permission.STOP_APP_SWITCHES;
+import static android.app.ActivityManager.DROP_CLOSE_SYSTEM_DIALOGS;
+import static android.app.ActivityManager.LOCK_DOWN_CLOSE_SYSTEM_DIALOGS;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
@@ -253,6 +255,7 @@
import com.android.server.inputmethod.InputMethodSystemProperty;
import com.android.server.pm.UserManagerService;
import com.android.server.policy.PermissionPolicyInternal;
+import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriGrantsManagerInternal;
@@ -342,6 +345,7 @@
/** The cached sys ui service component name from package manager. */
private ComponentName mSysUiServiceComponent;
private PermissionPolicyInternal mPermissionPolicyInternal;
+ private StatusBarManagerInternal mStatusBarManagerInternal;
@VisibleForTesting
final ActivityTaskManagerInternal mInternal;
PowerManagerInternal mPowerManagerInternal;
@@ -2670,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)) {
@@ -2927,14 +2931,12 @@
}
if (!canCloseSystemDialogs(pid, uid, process)) {
// The app can't close system dialogs, throw only if it targets S+
- if (CompatChanges.isChangeEnabled(
- ActivityManager.LOCK_DOWN_CLOSE_SYSTEM_DIALOGS, uid)) {
+ if (CompatChanges.isChangeEnabled(LOCK_DOWN_CLOSE_SYSTEM_DIALOGS, uid)) {
throw new SecurityException(
"Permission Denial: " + Intent.ACTION_CLOSE_SYSTEM_DIALOGS
+ " broadcast from " + caller + " requires "
+ Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS + ".");
- } else if (CompatChanges.isChangeEnabled(
- ActivityManager.DROP_CLOSE_SYSTEM_DIALOGS, uid)) {
+ } else if (CompatChanges.isChangeEnabled(DROP_CLOSE_SYSTEM_DIALOGS, uid)) {
Slog.e(TAG,
"Permission Denial: " + Intent.ACTION_CLOSE_SYSTEM_DIALOGS
+ " broadcast from " + caller + " requires "
@@ -2983,6 +2985,20 @@
return true;
}
}
+ // This covers the case where the app is displaying some UI on top of the notification shade
+ // and wants to start an activity. The app then sends the intent in order to move the
+ // notification shade out of the way and show the activity to the user. This is fine since
+ // the caller already has privilege to show a visible window on top of the notification
+ // shade, so it can already prevent the user from accessing the shade if it wants to.
+ // We only allow for targetSdk < S, for S+ we automatically collapse the shade on
+ // startActivity() for these apps.
+ if (!CompatChanges.isChangeEnabled(LOCK_DOWN_CLOSE_SYSTEM_DIALOGS, uid)) {
+ synchronized (mGlobalLock) {
+ if (mRootWindowContainer.hasVisibleWindowAboveNotificationShade(uid)) {
+ return true;
+ }
+ }
+ }
return false;
}
@@ -4787,6 +4803,13 @@
return mPermissionPolicyInternal;
}
+ StatusBarManagerInternal getStatusBarManagerInternal() {
+ if (mStatusBarManagerInternal == null) {
+ mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class);
+ }
+ return mStatusBarManagerInternal;
+ }
+
AppWarnings getAppWarningsLocked() {
return mAppWarnings;
}
@@ -4991,6 +5014,34 @@
}
}
+ /**
+ * Sets the corresponding {@link DisplayArea} information for the process global
+ * configuration. To be called when we need to show IME on a different {@link DisplayArea}
+ * or display.
+ *
+ * @param pid The process id associated with the IME window.
+ * @param imeContainer The DisplayArea that contains the IME window.
+ */
+ void onImeWindowSetOnDisplayArea(final int pid, @NonNull final DisplayArea imeContainer) {
+ // Don't update process-level configuration for Multi-Client IME process since other
+ // IMEs on other displays will also receive this configuration change due to IME
+ // services use the same application config/context.
+ if (InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED) return;
+
+ if (pid == MY_PID || pid < 0) {
+ ProtoLog.w(WM_DEBUG_CONFIGURATION,
+ "Trying to update display configuration for system/invalid process.");
+ return;
+ }
+ final WindowProcessController process = mProcessMap.getProcess(pid);
+ if (process == null) {
+ ProtoLog.w(WM_DEBUG_CONFIGURATION, "Trying to update display "
+ + "configuration for invalid process, pid=%d", pid);
+ return;
+ }
+ process.registerDisplayAreaConfigurationListener(imeContainer);
+ }
+
final class H extends Handler {
static final int REPORT_TIME_TRACKER_MSG = 1;
@@ -5494,44 +5545,6 @@
}
}
- /**
- * Set the corresponding display information for the process global configuration. To be
- * called when we need to show IME on a different display.
- *
- * @param pid The process id associated with the IME window.
- * @param displayId The ID of the display showing the IME.
- */
- @Override
- public void onImeWindowSetOnDisplay(final int pid, final int displayId) {
- // Don't update process-level configuration for Multi-Client IME process since other
- // IMEs on other displays will also receive this configuration change due to IME
- // services use the same application config/context.
- if (InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED) return;
-
- if (pid == MY_PID || pid < 0) {
- ProtoLog.w(WM_DEBUG_CONFIGURATION,
- "Trying to update display configuration for system/invalid process.");
- return;
- }
- synchronized (mGlobalLock) {
- final DisplayContent displayContent =
- mRootWindowContainer.getDisplayContent(displayId);
- if (displayContent == null) {
- // Call might come when display is not yet added or has been removed.
- ProtoLog.w(WM_DEBUG_CONFIGURATION, "Trying to update display "
- + "configuration for non-existing displayId=%d", displayId);
- return;
- }
- final WindowProcessController process = mProcessMap.getProcess(pid);
- if (process == null) {
- ProtoLog.w(WM_DEBUG_CONFIGURATION, "Trying to update display "
- + "configuration for invalid process, pid=%d", pid);
- return;
- }
- process.registerDisplayConfigurationListener(displayContent);
- }
- }
-
@Override
public void sendActivityResult(int callingUid, IBinder activityToken, String resultWho,
int requestCode, int resultCode, Intent data) {
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/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 1fd6d00..15483cb 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -564,22 +564,6 @@
}
@Override
- void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
- super.onParentChanged(newParent, oldParent);
- if (mOrganizer != null || newParent == null) {
- return;
- }
- // Check if we have a registered organizer, just after mSurfaceControl is ready.
- setOrganizer(mOrganizerController.getOrganizerByFeature(mFeatureId));
- }
-
- @Override
- void removeImmediately() {
- setOrganizer(null);
- super.removeImmediately();
- }
-
- @Override
DisplayArea getDisplayArea() {
return this;
}
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
index 38f78c9..53f7009 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -21,7 +21,6 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.DisplayArea.Type.ANY;
-import android.annotation.Nullable;
import android.content.pm.ParceledListSlice;
import android.os.Binder;
import android.os.IBinder;
@@ -51,10 +50,6 @@
private final WindowManagerGlobalLock mGlobalLock;
private final HashMap<Integer, IDisplayAreaOrganizer> mOrganizersByFeatureIds = new HashMap();
- @Nullable IDisplayAreaOrganizer getOrganizerByFeature(int featureId) {
- return mOrganizersByFeatureIds.get(featureId);
- }
-
private class DeathRecipient implements IBinder.DeathRecipient {
int mFeature;
IDisplayAreaOrganizer mOrganizer;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 5d79eb8..9769244 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -257,7 +257,7 @@
@Retention(RetentionPolicy.SOURCE)
@interface ForceScalingMode {}
- ActivityTaskManagerService mAtmService;
+ final ActivityTaskManagerService mAtmService;
/**
* Unique logical identifier of this display.
@@ -294,7 +294,7 @@
// window containers together and move them in-sync if/when needed. We use a subclass of
// WindowContainer which is omitted from screen magnification, as the IME is never magnified.
// TODO(display-area): is "no magnification" in the comment still true?
- private final ImeContainer mImeWindowsContainers = new ImeContainer(mWmService);
+ private final ImeContainer mImeWindowsContainer = new ImeContainer(mWmService);
@VisibleForTesting
final DisplayAreaPolicy mDisplayAreaPolicy;
@@ -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
@@ -1028,7 +1031,7 @@
// Setup the policy and build the display area hierarchy.
mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate(
- mWmService, this /* content */, this /* root */, mImeWindowsContainers);
+ mWmService, this /* content */, this /* root */, mImeWindowsContainer);
final List<DisplayArea<? extends WindowContainer>> areas =
mDisplayAreaPolicy.getDisplayAreas(FEATURE_WINDOWED_MAGNIFICATION);
@@ -1125,7 +1128,7 @@
switch (token.windowType) {
case TYPE_INPUT_METHOD:
case TYPE_INPUT_METHOD_DIALOG:
- mImeWindowsContainers.addChild(token);
+ mImeWindowsContainer.addChild(token);
break;
default:
mDisplayAreaPolicy.addWindow(token);
@@ -2410,7 +2413,7 @@
}
boolean forAllImeWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
- return mImeWindowsContainers.forAllWindowForce(callback, traverseTopToBottom);
+ return mImeWindowsContainer.forAllWindowForce(callback, traverseTopToBottom);
}
/**
@@ -3501,8 +3504,7 @@
// Update display configuration for IME process.
if (mInputMethodWindow != null) {
final int imePid = mInputMethodWindow.mSession.mPid;
- mWmService.mAtmInternal.onImeWindowSetOnDisplay(imePid,
- mInputMethodWindow.getDisplayId());
+ mAtmService.onImeWindowSetOnDisplayArea(imePid, mImeWindowsContainer);
}
mInsetsStateController.getSourceProvider(ITYPE_IME).setWindow(win,
mDisplayPolicy.getImeSourceFrameProvider(), null /* imeFrameProvider */);
@@ -3644,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();
}
/**
@@ -3733,7 +3738,7 @@
if (targetRoot != null) {
// Reposition the IME container to the target root to get the correct bounds and
// config.
- targetRoot.placeImeContainer(mImeWindowsContainers);
+ targetRoot.placeImeContainer(mImeWindowsContainer);
}
}
// 2. Reparent the IME container surface to either the input target app, or the IME window
@@ -3784,7 +3789,7 @@
final SurfaceControl newParent = computeImeParent();
if (newParent != null && newParent != mInputMethodSurfaceParent) {
mInputMethodSurfaceParent = newParent;
- getPendingTransaction().reparent(mImeWindowsContainers.mSurfaceControl, newParent);
+ getPendingTransaction().reparent(mImeWindowsContainer.mSurfaceControl, newParent);
scheduleAnimation();
}
}
@@ -3821,7 +3826,7 @@
}
// Otherwise, we just attach it to where the display area policy put it.
- return mImeWindowsContainers.getParent().getSurfaceControl();
+ return mImeWindowsContainer.getParent().getSurfaceControl();
}
void setLayoutNeeded() {
@@ -4192,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;
@@ -4201,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 */);
@@ -4569,7 +4580,7 @@
@Override
void assignChildLayers(SurfaceControl.Transaction t) {
- mImeWindowsContainers.setNeedsLayer();
+ mImeWindowsContainer.setNeedsLayer();
final WindowState imeTarget = mImeLayeringTarget;
// In the case where we have an IME target that is not in split-screen mode IME
// assignment is easy. We just need the IME to go directly above the target. This way
@@ -4592,7 +4603,7 @@
!(imeTarget.inMultiWindowMode()
|| imeTarget.mToken.isAppTransitioning()) && (
imeTarget.getSurfaceControl() != null))) {
- mImeWindowsContainers.assignRelativeLayer(t, imeTarget.getSurfaceControl(),
+ mImeWindowsContainer.assignRelativeLayer(t, imeTarget.getSurfaceControl(),
// TODO: We need to use an extra level on the app surface to ensure
// this is always above SurfaceView but always below attached window.
1);
@@ -4600,7 +4611,7 @@
// The IME surface parent may not be its window parent's surface
// (@see #computeImeParent), so set relative layer here instead of letting the window
// parent to assign layer.
- mImeWindowsContainers.assignRelativeLayer(t, mInputMethodSurfaceParent, 1);
+ mImeWindowsContainer.assignRelativeLayer(t, mInputMethodSurfaceParent, 1);
}
super.assignChildLayers(t);
}
@@ -4615,8 +4626,8 @@
* with {@link WindowState#assignLayer}
*/
void assignRelativeLayerForImeTargetChild(SurfaceControl.Transaction t, WindowContainer child) {
- mImeWindowsContainers.setNeedsLayer();
- child.assignRelativeLayer(t, mImeWindowsContainers.getSurfaceControl(), 1);
+ mImeWindowsContainer.setNeedsLayer();
+ child.assignRelativeLayer(t, mImeWindowsContainer.getSurfaceControl(), 1);
}
@Override
@@ -4883,7 +4894,7 @@
}
DisplayArea.Tokens getImeContainer() {
- return mImeWindowsContainers;
+ return mImeWindowsContainer;
}
SurfaceControl getOverlayLayer() {
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 0a95f8f..d8b1e18 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -36,6 +36,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
import static android.view.WindowManager.TRANSIT_NONE;
@@ -1544,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(
@@ -3137,6 +3140,27 @@
});
}
+ /**
+ * Returns {@code true} if {@code uid} has a visible window that's above a window of type {@link
+ * WindowManager.LayoutParams#TYPE_NOTIFICATION_SHADE}. If there is no window with type {@link
+ * WindowManager.LayoutParams#TYPE_NOTIFICATION_SHADE}, it returns {@code false}.
+ */
+ boolean hasVisibleWindowAboveNotificationShade(int uid) {
+ boolean[] visibleWindowFound = {false};
+ // We only return true if we found the notification shade (ie. window of type
+ // TYPE_NOTIFICATION_SHADE). Usually, it should always be there, but if for some reason
+ // it isn't, we should better be on the safe side and return false for this.
+ return forAllWindows(w -> {
+ if (w.mOwnerUid == uid && w.isVisible()) {
+ visibleWindowFound[0] = true;
+ }
+ if (w.mAttrs.type == TYPE_NOTIFICATION_SHADE) {
+ return visibleWindowFound[0];
+ }
+ return false;
+ }, true /* traverseTopToBottom */);
+ }
+
private boolean shouldCloseAssistant(ActivityRecord r, String reason) {
if (!r.isActivityTypeAssistant()) return false;
if (reason == SYSTEM_DIALOG_REASON_ASSIST) return false;
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/Session.java b/services/core/java/com/android/server/wm/Session.java
index 0cefa95..ff2509b 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -21,6 +21,7 @@
import static android.Manifest.permission.HIDE_OVERLAY_WINDOWS;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
@@ -104,6 +105,7 @@
// If non-system overlays from this process can be hidden by the user or app using
// HIDE_NON_SYSTEM_OVERLAY_WINDOWS.
final boolean mOverlaysCanBeHidden;
+ final boolean mCanCreateSystemApplicationOverlay;
final boolean mCanHideNonSystemOverlayWindows;
final boolean mCanAcquireSleepToken;
private AlertWindowNotification mAlertWindowNotification;
@@ -127,6 +129,9 @@
HIDE_NON_SYSTEM_OVERLAY_WINDOWS) == PERMISSION_GRANTED
|| service.mContext.checkCallingOrSelfPermission(HIDE_OVERLAY_WINDOWS)
== PERMISSION_GRANTED;
+ mCanCreateSystemApplicationOverlay =
+ service.mContext.checkCallingOrSelfPermission(SYSTEM_APPLICATION_OVERLAY)
+ == PERMISSION_GRANTED;
mOverlaysCanBeHidden = !mCanAddInternalSystemWindow
&& !mService.mAtmInternal.isCallerRecents(mUid);
mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER)
@@ -673,8 +678,8 @@
boolean changed;
- if (mOverlaysCanBeHidden) {
- // We want to track non-system signature apps adding alert windows so we can post an
+ if (mOverlaysCanBeHidden && !mCanCreateSystemApplicationOverlay) {
+ // We want to track non-system apps adding alert windows so we can post an
// on-going notification for the user to control their visibility.
if (visible) {
changed = mAlertWindowSurfaces.add(surfaceController);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 260b6c5..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();
@@ -4093,6 +4126,7 @@
info.topActivityInfo = mReuseActivitiesReport.top != null
? mReuseActivitiesReport.top.info
: null;
+ info.launchCookies.clear();
info.addLaunchCookie(mLaunchCookie);
forAllActivities(r -> {
info.addLaunchCookie(r.mLaunchCookie);
@@ -5016,6 +5050,11 @@
* @return {@code true} if task organizer changed.
*/
boolean updateTaskOrganizerState(boolean forceUpdate, boolean skipTaskAppeared) {
+ if (getSurfaceControl() == null) {
+ // Can't call onTaskAppeared without a surfacecontrol, so defer this until next one
+ // is created.
+ return false;
+ }
if (!canBeOrganized()) {
return setTaskOrganizer(null);
}
@@ -5025,10 +5064,6 @@
final ITaskOrganizer organizer = controller.getTaskOrganizer(windowingMode);
if (!forceUpdate && mTaskOrganizer == organizer) {
return false;
- } else if (organizer != null && getSurfaceControl() == null) {
- // Can't call onTaskAppeared without a surfacecontrol, so defer this until after one
- // is created.
- return false;
}
return setTaskOrganizer(organizer, skipTaskAppeared);
}
@@ -6033,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);
@@ -7164,6 +7197,7 @@
+ " mode=" + windowingModeToString(getWindowingMode()));
pw.println(" isSleeping=" + shouldSleepActivities());
pw.println(" mBounds=" + getRequestedOverrideBounds());
+ pw.println(" mCreatedByOrganizer=" + mCreatedByOrganizer);
};
boolean printed = false;
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 383dfd4..b3e0108 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -288,8 +288,14 @@
// possible.
while (!mOrganizedTasks.isEmpty()) {
final Task t = mOrganizedTasks.get(0);
- if (!t.updateTaskOrganizerState(true /* forceUpdate */)) {
- TaskOrganizerController.this.onTaskVanished(mOrganizer.mTaskOrganizer, 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);
+ }
}
}
@@ -486,22 +492,26 @@
void onTaskVanished(ITaskOrganizer organizer, Task task) {
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
if (state != null && state.removeTask(task)) {
- for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) {
- PendingTaskEvent entry = mPendingTaskEvents.get(i);
- if (task.mTaskId == entry.mTask.mTaskId) {
- // This task will vanished so remove all pending event of it.
- mPendingTaskEvents.remove(i);
- if (entry.mEventType == PendingTaskEvent.EVENT_APPEARED) {
- // If task still not appeared, ignore this callback.
- return;
- }
+ 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);
}
+
+ PendingTaskEvent pending =
+ new PendingTaskEvent(task, organizer, PendingTaskEvent.EVENT_VANISHED);
+ mPendingTaskEvents.add(pending);
}
@Override
@@ -653,6 +663,7 @@
changed = (cfgChanges & REPORT_CONFIGS) != 0;
}
if (!(changed || force)) {
+ // mTmpTaskInfo will be reused next time.
return;
}
final RunningTaskInfo newInfo = mTmpTaskInfo;
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/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/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 663d91e..389f428 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -20,7 +20,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.os.Build.VERSION_CODES.Q;
import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
-import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.util.Preconditions.checkArgument;
@@ -201,8 +200,13 @@
private final Configuration mLastReportedConfiguration = new Configuration();
/** Whether the process configuration is waiting to be dispatched to the process. */
private boolean mHasPendingConfigurationChange;
- // Registered display id as a listener to override config change
- private int mDisplayId;
+
+ /**
+ * Registered {@link DisplayArea} as a listener to override config changes. {@code null} if not
+ * registered.
+ */
+ @Nullable
+ private DisplayArea mDisplayArea;
private ActivityRecord mConfigActivityRecord;
// Whether the activity config override is allowed for this process.
private volatile boolean mIsActivityConfigOverrideAllowed = true;
@@ -252,7 +256,6 @@
mOwner = owner;
mListener = listener;
mAtm = atm;
- mDisplayId = INVALID_DISPLAY;
mBackgroundActivityStartCallback = mAtm.getBackgroundActivityStartCallback();
boolean isSysUiPackage = info.packageName.equals(
@@ -393,9 +396,9 @@
return mPendingUiClean;
}
- /** @return {@code true} if the process registered to a display as a config listener. */
- boolean registeredForDisplayConfigChanges() {
- return mDisplayId != INVALID_DISPLAY;
+ /** @return {@code true} if the process registered to a display area as a config listener. */
+ boolean registeredForDisplayAreaConfigChanges() {
+ return mDisplayArea != null;
}
/** @return {@code true} if the process registered to an activity as a config listener. */
@@ -443,11 +446,14 @@
return mRequiredAbi;
}
- /** Returns ID of display overriding the configuration for this process, or
- * INVALID_DISPLAY if no display is overriding. */
+ /**
+ * Registered {@link DisplayArea} as a listener to override config changes. {@code null} if not
+ * registered.
+ */
@VisibleForTesting
- int getDisplayId() {
- return mDisplayId;
+ @Nullable
+ DisplayArea getDisplayArea() {
+ return mDisplayArea;
}
public void setDebugging(boolean debugging) {
@@ -1317,29 +1323,22 @@
return hasVisibleActivities;
}
- void registerDisplayConfigurationListener(DisplayContent displayContent) {
- if (displayContent == null) {
+ void registerDisplayAreaConfigurationListener(@Nullable DisplayArea displayArea) {
+ if (displayArea == null || displayArea.containsListener(this)) {
return;
}
- // A process can only register to one display to listen to the override configuration
- // change. Unregister existing listener if it has one before register the new one.
- unregisterDisplayConfigurationListener();
- unregisterActivityConfigurationListener();
- mDisplayId = displayContent.mDisplayId;
- displayContent.registerConfigurationChangeListener(this);
+ unregisterConfigurationListeners();
+ mDisplayArea = displayArea;
+ displayArea.registerConfigurationChangeListener(this);
}
@VisibleForTesting
- void unregisterDisplayConfigurationListener() {
- if (mDisplayId == INVALID_DISPLAY) {
+ void unregisterDisplayAreaConfigurationListener() {
+ if (mDisplayArea == null) {
return;
}
- final DisplayContent displayContent =
- mAtm.mRootWindowContainer.getDisplayContent(mDisplayId);
- if (displayContent != null) {
- displayContent.unregisterConfigurationChangeListener(this);
- }
- mDisplayId = INVALID_DISPLAY;
+ mDisplayArea.unregisterConfigurationChangeListener(this);
+ mDisplayArea = null;
onMergedOverrideConfigurationChanged(Configuration.EMPTY);
}
@@ -1349,10 +1348,7 @@
|| !mIsActivityConfigOverrideAllowed) {
return;
}
- // A process can only register to one activityRecord to listen to the override configuration
- // change. Unregister existing listener if it has one before register the new one.
- unregisterDisplayConfigurationListener();
- unregisterActivityConfigurationListener();
+ unregisterConfigurationListeners();
mConfigActivityRecord = activityRecord;
activityRecord.registerConfigurationChangeListener(this);
}
@@ -1367,6 +1363,16 @@
}
/**
+ * A process can only register to one {@link WindowContainer} to listen to the override
+ * configuration changes. Unregisters the existing listener if it has one before registers a
+ * new one.
+ */
+ private void unregisterConfigurationListeners() {
+ unregisterActivityConfigurationListener();
+ unregisterDisplayAreaConfigurationListener();
+ }
+
+ /**
* Check if activity configuration override for the activity process needs an update and perform
* if needed. By default we try to override the process configuration to match the top activity
* config to increase app compatibility with multi-window and multi-display. The process will
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b35e3e4..13b9765 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -67,10 +67,12 @@
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
@@ -211,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;
@@ -643,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
@@ -723,7 +731,7 @@
static final int BLAST_TIMEOUT_DURATION = 5000; /* milliseconds */
- private final WindowProcessController mWpcForDisplayConfigChanges;
+ private final WindowProcessController mWpcForDisplayAreaConfigChanges;
/**
* Returns the visibility of the given {@link InternalInsetsType type} requested by the client.
@@ -915,7 +923,7 @@
mBaseLayer = 0;
mSubLayer = 0;
mWinAnimator = null;
- mWpcForDisplayConfigChanges = null;
+ mWpcForDisplayAreaConfigChanges = null;
return;
}
mDeathRecipient = deathRecipient;
@@ -969,8 +977,8 @@
parentWindow.addChild(this, sWindowSubLayerComparator);
}
- // System process or invalid process cannot register to display config change.
- mWpcForDisplayConfigChanges = (s.mPid == MY_PID || s.mPid < 0)
+ // System process or invalid process cannot register to display area config change.
+ mWpcForDisplayAreaConfigChanges = (s.mPid == MY_PID || s.mPid < 0)
? null
: service.mAtmService.getProcessController(s.mPid, s.mUid);
}
@@ -2095,7 +2103,7 @@
return getDisplayContent().getBounds().equals(getBounds());
}
- private boolean matchesRootDisplayAreaBounds() {
+ boolean matchesRootDisplayAreaBounds() {
RootDisplayArea root = getRootDisplayArea();
if (root == null || root == getDisplayContent()) {
return matchesDisplayBounds();
@@ -3034,6 +3042,12 @@
|| (!isSystemAlertWindowType(mAttrs.type) && mAttrs.type != TYPE_TOAST)) {
return;
}
+
+ if (mAttrs.type == TYPE_APPLICATION_OVERLAY && mSession.mCanCreateSystemApplicationOverlay
+ && (mAttrs.privateFlags & SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY) != 0) {
+ return;
+ }
+
if (mForceHideNonSystemOverlayWindow == forceHide) {
return;
}
@@ -3525,9 +3539,9 @@
return mActivityRecord.mFrozenMergedConfig.peek();
}
- // If the process has not registered to any display to listen to the configuration change,
- // we can simply return the mFullConfiguration as default.
- if (!registeredForDisplayConfigChanges()) {
+ // If the process has not registered to any display area to listen to the configuration
+ // change, we can simply return the mFullConfiguration as default.
+ if (!registeredForDisplayAreaConfigChanges()) {
return super.getConfiguration();
}
@@ -3538,13 +3552,13 @@
return mTempConfiguration;
}
- /** @return {@code true} if the process registered to a display as a config listener. */
- private boolean registeredForDisplayConfigChanges() {
+ /** @return {@code true} if the process registered to a display area as a config listener. */
+ private boolean registeredForDisplayAreaConfigChanges() {
final WindowState parentWindow = getParentWindow();
final WindowProcessController wpc = parentWindow != null
- ? parentWindow.mWpcForDisplayConfigChanges
- : mWpcForDisplayConfigChanges;
- return wpc != null && wpc.registeredForDisplayConfigChanges();
+ ? parentWindow.mWpcForDisplayAreaConfigChanges
+ : mWpcForDisplayAreaConfigChanges;
+ return wpc != null && wpc.registeredForDisplayAreaConfigChanges();
}
void fillClientWindowFrames(ClientWindowFrames outFrames) {
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_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 35aad3e..b2d6b15 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -2709,7 +2709,7 @@
}
static jboolean android_location_gnss_hal_GnssNative_start_measurement_collection(
- JNIEnv* /* env */, jclass, jboolean enableFullTracking) {
+ JNIEnv* /* env */, jclass, jboolean enableFullTracking, jboolean enableCorrVecOutputs) {
if (gnssMeasurementIface == nullptr) {
ALOGE("%s: IGnssMeasurement interface not available.", __func__);
return JNI_FALSE;
@@ -2717,7 +2717,7 @@
return gnssMeasurementIface->setCallback(std::make_unique<gnss::GnssMeasurementCallback>(
mCallbacksObj),
- enableFullTracking);
+ enableFullTracking, enableCorrVecOutputs);
}
static jboolean android_location_gnss_hal_GnssNative_stop_measurement_collection(JNIEnv* env,
@@ -3211,7 +3211,7 @@
/* name, signature, funcPtr */
{"native_is_measurement_supported", "()Z",
reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_is_measurement_supported)},
- {"native_start_measurement_collection", "(Z)Z",
+ {"native_start_measurement_collection", "(ZZ)Z",
reinterpret_cast<void*>(
android_location_gnss_hal_GnssNative_start_measurement_collection)},
{"native_stop_measurement_collection", "()Z",
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 631e185..7b379e5 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -68,6 +68,9 @@
static constexpr auto PollTimeoutMs = 5000;
static constexpr auto TraceTagCheckInterval = 1s;
+static constexpr auto WaitOnEofMinInterval = 10ms;
+static constexpr auto WaitOnEofMaxInterval = 1s;
+
struct JniIds {
jclass packageManagerShellCommandDataLoader;
jmethodID pmscdLookupShellCommand;
@@ -485,14 +488,16 @@
if (read == 0) {
if (waitOnEof) {
// eof of stdin, waiting...
- ALOGE("eof of stdin, waiting...: %d, remaining: %d, block: %d, read: %d",
- int(totalSize), int(remaining), int(blockIdx), int(read));
- using namespace std::chrono_literals;
- std::this_thread::sleep_for(10ms);
- continue;
+ if (doWaitOnEof()) {
+ continue;
+ } else {
+ return false;
+ }
}
break;
}
+ resetWaitOnEof();
+
if (read < 0) {
return false;
}
@@ -776,6 +781,21 @@
return fileId;
}
+ // Waiting with exponential backoff, maximum total time ~1.2sec.
+ bool doWaitOnEof() {
+ if (mWaitOnEofInterval >= WaitOnEofMaxInterval) {
+ resetWaitOnEof();
+ return false;
+ }
+ auto result = mWaitOnEofInterval;
+ mWaitOnEofInterval =
+ std::min<std::chrono::milliseconds>(mWaitOnEofInterval * 2, WaitOnEofMaxInterval);
+ std::this_thread::sleep_for(result);
+ return true;
+ }
+
+ void resetWaitOnEof() { mWaitOnEofInterval = WaitOnEofMinInterval; }
+
JavaVM* const mJvm;
std::string mArgs;
android::dataloader::FilesystemConnectorPtr mIfs = nullptr;
@@ -786,6 +806,7 @@
std::thread mReceiverThread;
std::atomic<bool> mStopReceiving = false;
std::atomic<bool> mReadLogsEnabled = false;
+ std::chrono::milliseconds mWaitOnEofInterval{WaitOnEofMinInterval};
/** Tracks which files have been requested */
std::unordered_set<FileIdx> mRequestedFiles;
};
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/core/jni/gnss/GnssMeasurement.cpp b/services/core/jni/gnss/GnssMeasurement.cpp
index 2261c38..663d839 100644
--- a/services/core/jni/gnss/GnssMeasurement.cpp
+++ b/services/core/jni/gnss/GnssMeasurement.cpp
@@ -50,8 +50,9 @@
: mIGnssMeasurement(iGnssMeasurement) {}
jboolean GnssMeasurement::setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking) {
- auto status = mIGnssMeasurement->setCallback(callback->getAidl(), enableFullTracking);
+ bool enableFullTracking, bool enableCorrVecOutputs) {
+ auto status = mIGnssMeasurement->setCallback(callback->getAidl(), enableFullTracking,
+ enableCorrVecOutputs);
return checkAidlStatus(status, "IGnssMeasurement setCallback() failed.");
}
@@ -66,9 +67,12 @@
: mIGnssMeasurement_V1_0(iGnssMeasurement) {}
jboolean GnssMeasurement_V1_0::setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking) {
+ bool enableFullTracking, bool enableCorrVecOutputs) {
if (enableFullTracking == true) {
- ALOGW("Full tracking is mode not supported in 1.0 GNSS HAL.");
+ ALOGW("Full tracking mode is not supported in 1.0 GNSS HAL.");
+ }
+ if (enableCorrVecOutputs == true) {
+ ALOGW("Correlation vector output is not supported in 1.0 GNSS HAL.");
}
auto status = mIGnssMeasurement_V1_0->setCallback(callback->getHidl());
if (!checkHidlReturn(status, "IGnssMeasurement setCallback() failed.")) {
@@ -89,7 +93,10 @@
: GnssMeasurement_V1_0{iGnssMeasurement}, mIGnssMeasurement_V1_1(iGnssMeasurement) {}
jboolean GnssMeasurement_V1_1::setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking) {
+ bool enableFullTracking, bool enableCorrVecOutputs) {
+ if (enableCorrVecOutputs == true) {
+ ALOGW("Correlation vector output is not supported in 1.1 GNSS HAL.");
+ }
auto status = mIGnssMeasurement_V1_1->setCallback_1_1(callback->getHidl(), enableFullTracking);
if (!checkHidlReturn(status, "IGnssMeasurement setCallback_V1_1() failed.")) {
return JNI_FALSE;
@@ -104,7 +111,10 @@
: GnssMeasurement_V1_1{iGnssMeasurement}, mIGnssMeasurement_V2_0(iGnssMeasurement) {}
jboolean GnssMeasurement_V2_0::setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking) {
+ bool enableFullTracking, bool enableCorrVecOutputs) {
+ if (enableCorrVecOutputs == true) {
+ ALOGW("Correlation vector output is not supported in 2.0 GNSS HAL.");
+ }
auto status = mIGnssMeasurement_V2_0->setCallback_2_0(callback->getHidl(), enableFullTracking);
if (!checkHidlReturn(status, "IGnssMeasurement setCallback_2_0() failed.")) {
return JNI_FALSE;
@@ -119,7 +129,10 @@
: GnssMeasurement_V2_0{iGnssMeasurement}, mIGnssMeasurement_V2_1(iGnssMeasurement) {}
jboolean GnssMeasurement_V2_1::setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking) {
+ bool enableFullTracking, bool enableCorrVecOutputs) {
+ if (enableCorrVecOutputs == true) {
+ ALOGW("Correlation vector output is not supported in 2.1 GNSS HAL.");
+ }
auto status = mIGnssMeasurement_V2_1->setCallback_2_1(callback->getHidl(), enableFullTracking);
if (!checkHidlReturn(status, "IGnssMeasurement setCallback_2_1() failed.")) {
return JNI_FALSE;
diff --git a/services/core/jni/gnss/GnssMeasurement.h b/services/core/jni/gnss/GnssMeasurement.h
index e64336f..f0752cd 100644
--- a/services/core/jni/gnss/GnssMeasurement.h
+++ b/services/core/jni/gnss/GnssMeasurement.h
@@ -38,7 +38,7 @@
public:
virtual ~GnssMeasurementInterface() {}
virtual jboolean setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking) = 0;
+ bool enableFullTracking, bool enableCorrVecOutputs) = 0;
virtual jboolean close() = 0;
};
@@ -46,7 +46,7 @@
public:
GnssMeasurement(const sp<android::hardware::gnss::IGnssMeasurementInterface>& iGnssMeasurement);
jboolean setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking) override;
+ bool enableFullTracking, bool enableCorrVecOutputs) override;
jboolean close() override;
private:
@@ -58,7 +58,7 @@
GnssMeasurement_V1_0(
const sp<android::hardware::gnss::V1_0::IGnssMeasurement>& iGnssMeasurement);
jboolean setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking) override;
+ bool enableFullTracking, bool enableCorrVecOutputs) override;
jboolean close() override;
private:
@@ -70,7 +70,7 @@
GnssMeasurement_V1_1(
const sp<android::hardware::gnss::V1_1::IGnssMeasurement>& iGnssMeasurement);
jboolean setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking) override;
+ bool enableFullTracking, bool enableCorrVecOutputs) override;
private:
const sp<android::hardware::gnss::V1_1::IGnssMeasurement> mIGnssMeasurement_V1_1;
@@ -81,7 +81,7 @@
GnssMeasurement_V2_0(
const sp<android::hardware::gnss::V2_0::IGnssMeasurement>& iGnssMeasurement);
jboolean setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking) override;
+ bool enableFullTracking, bool enableCorrVecOutputs) override;
private:
const sp<android::hardware::gnss::V2_0::IGnssMeasurement> mIGnssMeasurement_V2_0;
@@ -92,7 +92,7 @@
GnssMeasurement_V2_1(
const sp<android::hardware::gnss::V2_1::IGnssMeasurement>& iGnssMeasurement);
jboolean setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking) override;
+ bool enableFullTracking, bool enableCorrVecOutputs) override;
private:
const sp<android::hardware::gnss::V2_1::IGnssMeasurement> mIGnssMeasurement_V2_1;
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.cpp b/services/core/jni/gnss/GnssMeasurementCallback.cpp
index 8cba773..757381d 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.cpp
+++ b/services/core/jni/gnss/GnssMeasurementCallback.cpp
@@ -21,21 +21,33 @@
namespace android::gnss {
using binder::Status;
+using hardware::gnss::CorrelationVector;
using hardware::gnss::ElapsedRealtime;
using hardware::gnss::GnssClock;
using hardware::gnss::GnssData;
using hardware::gnss::GnssMeasurement;
using hardware::gnss::SatellitePvt;
+jclass class_arrayList;
+jclass class_clockInfo;
+jclass class_correlationVectorBuilder;
jclass class_gnssMeasurementsEvent;
jclass class_gnssMeasurement;
jclass class_gnssClock;
-jclass class_satellitePvtBuilder;
jclass class_positionEcef;
+jclass class_satellitePvtBuilder;
jclass class_velocityEcef;
-jclass class_clockInfo;
+jmethodID method_arrayListAdd;
+jmethodID method_arrayListCtor;
+jmethodID method_correlationVectorBuilderBuild;
+jmethodID method_correlationVectorBuilderCtor;
+jmethodID method_correlationVectorBuilderSetFrequencyOffsetMetersPerSecond;
+jmethodID method_correlationVectorBuilderSetMagnitude;
+jmethodID method_correlationVectorBuilderSetSamplingStartMeters;
+jmethodID method_correlationVectorBuilderSetSamplingWidthMeters;
jmethodID method_gnssMeasurementsEventCtor;
+jmethodID method_gnssMeasurementsSetCorrelationVectors;
jmethodID method_gnssMeasurementsSetSatellitePvt;
jmethodID method_gnssClockCtor;
jmethodID method_gnssMeasurementCtor;
@@ -66,6 +78,9 @@
method_gnssMeasurementsSetSatellitePvt =
env->GetMethodID(class_gnssMeasurement, "setSatellitePvt",
"(Landroid/location/SatellitePvt;)V");
+ method_gnssMeasurementsSetCorrelationVectors =
+ env->GetMethodID(class_gnssMeasurement, "setCorrelationVectors",
+ "(Ljava/util/Collection;)V");
jclass gnssClockClass = env->FindClass("android/location/GnssClock");
class_gnssClock = (jclass)env->NewGlobalRef(gnssClockClass);
@@ -106,6 +121,31 @@
jclass clockInfoClass = env->FindClass("android/location/SatellitePvt$ClockInfo");
class_clockInfo = (jclass)env->NewGlobalRef(clockInfoClass);
method_clockInfo = env->GetMethodID(class_clockInfo, "<init>", "(DDD)V");
+
+ jclass correlationVectorBuilder = env->FindClass("android/location/CorrelationVector$Builder");
+ class_correlationVectorBuilder = (jclass)env->NewGlobalRef(correlationVectorBuilder);
+ method_correlationVectorBuilderCtor =
+ env->GetMethodID(class_correlationVectorBuilder, "<init>", "()V");
+ method_correlationVectorBuilderSetMagnitude =
+ env->GetMethodID(class_correlationVectorBuilder, "setMagnitude",
+ "([I)Landroid/location/CorrelationVector$Builder;");
+ method_correlationVectorBuilderSetFrequencyOffsetMetersPerSecond =
+ env->GetMethodID(class_correlationVectorBuilder, "setFrequencyOffsetMetersPerSecond",
+ "(I)Landroid/location/CorrelationVector$Builder;");
+ method_correlationVectorBuilderSetSamplingStartMeters =
+ env->GetMethodID(class_correlationVectorBuilder, "setSamplingStartMeters",
+ "(D)Landroid/location/CorrelationVector$Builder;");
+ method_correlationVectorBuilderSetSamplingWidthMeters =
+ env->GetMethodID(class_correlationVectorBuilder, "setSamplingWidthMeters",
+ "(D)Landroid/location/CorrelationVector$Builder;");
+ method_correlationVectorBuilderBuild =
+ env->GetMethodID(class_correlationVectorBuilder, "build",
+ "()Landroid/location/CorrelationVector;");
+
+ jclass arrayListClass = env->FindClass("java/util/ArrayList");
+ class_arrayList = (jclass)env->NewGlobalRef(arrayListClass);
+ method_arrayListCtor = env->GetMethodID(class_arrayList, "<init>", "()V");
+ method_arrayListAdd = env->GetMethodID(class_arrayList, "add", "(Ljava/lang/Object;)Z");
}
void setMeasurementData(JNIEnv* env, jobject& callbacksObj, jobject clock,
@@ -310,6 +350,47 @@
env->DeleteLocalRef(satellitePvtObject);
}
+ if (measurement.flags & static_cast<uint32_t>(GnssMeasurement::HAS_CORRELATION_VECTOR)) {
+ jobject correlationVectorList = env->NewObject(class_arrayList, method_arrayListCtor);
+ for (uint16_t i = 0; i < measurement.correlationVectors.size(); ++i) {
+ const CorrelationVector& correlationVector = measurement.correlationVectors[i];
+ const std::vector<int32_t>& magnitudeVector = correlationVector.magnitude;
+
+ jsize numMagnitude = magnitudeVector.size();
+ jintArray magnitudeArray = env->NewIntArray(numMagnitude);
+ env->SetIntArrayRegion(magnitudeArray, 0, numMagnitude,
+ reinterpret_cast<const jint*>(magnitudeVector.data()));
+
+ jobject correlationVectorBuilderObject =
+ env->NewObject(class_correlationVectorBuilder,
+ method_correlationVectorBuilderCtor);
+ env->CallObjectMethod(correlationVectorBuilderObject,
+ method_correlationVectorBuilderSetMagnitude, magnitudeArray);
+ env->CallObjectMethod(correlationVectorBuilderObject,
+ method_correlationVectorBuilderSetFrequencyOffsetMetersPerSecond,
+ correlationVector.frequencyOffsetMps);
+ env->CallObjectMethod(correlationVectorBuilderObject,
+ method_correlationVectorBuilderSetSamplingStartMeters,
+ correlationVector.samplingStartM);
+ env->CallObjectMethod(correlationVectorBuilderObject,
+ method_correlationVectorBuilderSetSamplingWidthMeters,
+ correlationVector.samplingWidthM);
+ jobject correlationVectorObject =
+ env->CallObjectMethod(correlationVectorBuilderObject,
+ method_correlationVectorBuilderBuild);
+
+ env->CallBooleanMethod(correlationVectorList, method_arrayListAdd,
+ correlationVectorObject);
+
+ env->DeleteLocalRef(magnitudeArray);
+ env->DeleteLocalRef(correlationVectorBuilderObject);
+ env->DeleteLocalRef(correlationVectorObject);
+ }
+ env->CallVoidMethod(object.get(), method_gnssMeasurementsSetCorrelationVectors,
+ correlationVectorList);
+ env->DeleteLocalRef(correlationVectorList);
+ }
+
jstring codeType = env->NewStringUTF(measurement.signalType.codeType.c_str());
SET(CodeType, codeType);
env->DeleteLocalRef(codeType);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index 5e7f984..ba3ae45 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -16,6 +16,7 @@
package com.android.server.devicepolicy;
+import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyManager;
@@ -24,6 +25,7 @@
import android.os.PersistableBundle;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.DebugUtils;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.TypedXmlPullParser;
@@ -85,6 +87,15 @@
static final String NEW_USER_DISCLAIMER_NOT_NEEDED = "not_needed";
static final String NEW_USER_DISCLAIMER_NEEDED = "needed";
+ private static final String ATTR_FACTORY_RESET_FLAGS = "factory-reset-flags";
+ private static final String ATTR_FACTORY_RESET_REASON = "factory-reset-reason";
+
+ // NOTE: must be public because of DebugUtils.flagsToString()
+ public static final int FACTORY_RESET_FLAG_ON_BOOT = 1;
+ public static final int FACTORY_RESET_FLAG_WIPE_EXTERNAL_STORAGE = 2;
+ public static final int FACTORY_RESET_FLAG_WIPE_EUICC = 4;
+ public static final int FACTORY_RESET_FLAG_WIPE_FACTORY_RESET_PROTECTION = 8;
+
private static final String TAG = DevicePolicyManagerService.LOG_TAG;
private static final boolean VERBOSE_LOG = false; // DO NOT SUBMIT WITH TRUE
@@ -99,6 +110,9 @@
int mUserProvisioningState;
int mPermissionPolicy;
+ int mFactoryResetFlags;
+ String mFactoryResetReason;
+
boolean mDeviceProvisioningConfigApplied = false;
final ArrayMap<ComponentName, ActiveAdmin> mAdminMap = new ArrayMap<>();
@@ -200,6 +214,17 @@
out.attribute(null, ATTR_NEW_USER_DISCLAIMER, policyData.mNewUserDisclaimer);
}
+ if (policyData.mFactoryResetFlags != 0) {
+ if (VERBOSE_LOG) {
+ Slog.v(TAG, "Storing factory reset flags for user " + policyData.mUserId + ": "
+ + factoryResetFlagsToString(policyData.mFactoryResetFlags));
+ }
+ out.attributeInt(null, ATTR_FACTORY_RESET_FLAGS, policyData.mFactoryResetFlags);
+ }
+ if (policyData.mFactoryResetReason != null) {
+ out.attribute(null, ATTR_FACTORY_RESET_REASON, policyData.mFactoryResetReason);
+ }
+
// Serialize delegations.
for (int i = 0; i < policyData.mDelegationMap.size(); ++i) {
final String delegatePackage = policyData.mDelegationMap.keyAt(i);
@@ -427,6 +452,13 @@
}
policy.mNewUserDisclaimer = parser.getAttributeValue(null, ATTR_NEW_USER_DISCLAIMER);
+ policy.mFactoryResetFlags = parser.getAttributeInt(null, ATTR_FACTORY_RESET_FLAGS, 0);
+ if (VERBOSE_LOG) {
+ Slog.v(TAG, "Restored factory reset flags for user " + policy.mUserId + ": "
+ + factoryResetFlagsToString(policy.mFactoryResetFlags));
+ }
+ policy.mFactoryResetReason = parser.getAttributeValue(null, ATTR_FACTORY_RESET_REASON);
+
int outerDepth = parser.getDepth();
policy.mLockTaskPackages.clear();
policy.mAdminList.clear();
@@ -572,6 +604,22 @@
}
}
+ void setDelayedFactoryReset(@NonNull String reason, boolean wipeExtRequested, boolean wipeEuicc,
+ boolean wipeResetProtectionData) {
+ mFactoryResetReason = reason;
+
+ mFactoryResetFlags = FACTORY_RESET_FLAG_ON_BOOT;
+ if (wipeExtRequested) {
+ mFactoryResetFlags |= FACTORY_RESET_FLAG_WIPE_EXTERNAL_STORAGE;
+ }
+ if (wipeEuicc) {
+ mFactoryResetFlags |= FACTORY_RESET_FLAG_WIPE_EUICC;
+ }
+ if (wipeResetProtectionData) {
+ mFactoryResetFlags |= FACTORY_RESET_FLAG_WIPE_FACTORY_RESET_PROTECTION;
+ }
+ }
+
void dump(IndentingPrintWriter pw) {
pw.println();
pw.println("Enabled Device Admins (User " + mUserId + ", provisioningState: "
@@ -603,6 +651,19 @@
pw.print("mUserSetupComplete="); pw.println(mUserSetupComplete);
pw.print("mAffiliationIds="); pw.println(mAffiliationIds);
pw.print("mNewUserDisclaimer="); pw.println(mNewUserDisclaimer);
+ if (mFactoryResetFlags != 0) {
+ pw.print("mFactoryResetFlags="); pw.print(mFactoryResetFlags);
+ pw.print(" (");
+ pw.print(factoryResetFlagsToString(mFactoryResetFlags));
+ pw.println(')');
+ }
+ if (mFactoryResetReason != null) {
+ pw.print("mFactoryResetReason="); pw.println(mFactoryResetReason);
+ }
pw.decreaseIndent();
}
+
+ static String factoryResetFlagsToString(int flags) {
+ return DebugUtils.flagsToString(DevicePolicyData.class, "FACTORY_RESET_FLAG_", flags);
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4a311d3b..7df9016 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1320,12 +1320,11 @@
mContext.getSystemService(PowerManager.class).reboot(reason);
}
- void recoverySystemRebootWipeUserData(boolean shutdown, String reason, boolean force,
+ boolean recoverySystemRebootWipeUserData(boolean shutdown, String reason, boolean force,
boolean wipeEuicc, boolean wipeExtRequested, boolean wipeResetProtectionData)
throws IOException {
- FactoryResetter.newBuilder(mContext).setSafetyChecker(mSafetyChecker)
- .setReason(reason).setShutdown(shutdown)
- .setForce(force).setWipeEuicc(wipeEuicc)
+ return FactoryResetter.newBuilder(mContext).setSafetyChecker(mSafetyChecker)
+ .setReason(reason).setShutdown(shutdown).setForce(force).setWipeEuicc(wipeEuicc)
.setWipeAdoptableStorage(wipeExtRequested)
.setWipeFactoryResetProtection(wipeResetProtectionData)
.build().factoryReset();
@@ -2787,6 +2786,10 @@
maybeStartSecurityLogMonitorOnActivityManagerReady();
break;
case SystemService.PHASE_BOOT_COMPLETED:
+ // Ideally it should be done earlier, but currently it relies on RecoverySystem,
+ // which would hang on earlier phases
+ factoryResetIfDelayedEarlier();
+
ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this.
break;
}
@@ -3380,6 +3383,7 @@
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REMOVE_ACTIVE_ADMIN);
enforceUserUnlocked(userHandle);
synchronized (getLockObject()) {
@@ -5140,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(
@@ -5171,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);
@@ -5200,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 {
@@ -5257,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 {
@@ -6154,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(() -> {
@@ -6261,10 +6270,24 @@
boolean wipeResetProtectionData) {
wtfIfInLock();
boolean success = false;
+
try {
- mInjector.recoverySystemRebootWipeUserData(
+ boolean delayed = !mInjector.recoverySystemRebootWipeUserData(
/* shutdown= */ false, reason, /* force= */ true, /* wipeEuicc= */ wipeEuicc,
wipeExtRequested, wipeResetProtectionData);
+ if (delayed) {
+ // Persist the request so the device is automatically factory-reset on next start if
+ // the system crashes or reboots before the {@code DevicePolicySafetyChecker} calls
+ // its callback.
+ Slog.i(LOG_TAG, String.format("Persisting factory reset request as it could be "
+ + "delayed by %s", mSafetyChecker));
+ synchronized (getLockObject()) {
+ DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
+ policy.setDelayedFactoryReset(reason, wipeExtRequested, wipeEuicc,
+ wipeResetProtectionData);
+ saveSettingsLocked(UserHandle.USER_SYSTEM);
+ }
+ }
success = true;
} catch (IOException | SecurityException e) {
Slog.w(LOG_TAG, "Failed requesting data wipe", e);
@@ -6273,6 +6296,40 @@
}
}
+ private void factoryResetIfDelayedEarlier() {
+ synchronized (getLockObject()) {
+ DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
+
+ if (policy.mFactoryResetFlags == 0) return;
+
+ if (policy.mFactoryResetReason == null) {
+ // Shouldn't happen.
+ Slog.e(LOG_TAG, "no persisted reason for factory resetting");
+ policy.mFactoryResetReason = "requested before boot";
+ }
+ FactoryResetter factoryResetter = FactoryResetter.newBuilder(mContext)
+ .setReason(policy.mFactoryResetReason).setForce(true)
+ .setWipeEuicc((policy.mFactoryResetFlags & DevicePolicyData
+ .FACTORY_RESET_FLAG_WIPE_EUICC) != 0)
+ .setWipeAdoptableStorage((policy.mFactoryResetFlags & DevicePolicyData
+ .FACTORY_RESET_FLAG_WIPE_EXTERNAL_STORAGE) != 0)
+ .setWipeFactoryResetProtection((policy.mFactoryResetFlags & DevicePolicyData
+ .FACTORY_RESET_FLAG_WIPE_FACTORY_RESET_PROTECTION) != 0)
+ .build();
+ Slog.i(LOG_TAG, "Factory resetting on boot using " + factoryResetter);
+ try {
+ if (!factoryResetter.factoryReset()) {
+ // Shouldn't happen because FactoryResetter was created without a
+ // DevicePolicySafetyChecker.
+ Slog.wtf(LOG_TAG, "Factory reset using " + factoryResetter + " failed.");
+ }
+ } catch (IOException e) {
+ // Shouldn't happen.
+ Slog.wtf(LOG_TAG, "Could not factory reset using " + factoryResetter, e);
+ }
+ }
+ }
+
private void forceWipeUser(int userId, String wipeReasonForUser, boolean wipeSilently) {
boolean success = false;
try {
@@ -6449,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()) {
@@ -7346,6 +7405,7 @@
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller));
checkAllUsersAreAffiliatedWithDevice();
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REQUEST_BUGREPORT);
if (mBugreportCollectionManager.requestBugreport()) {
DevicePolicyEventLogger
@@ -7455,6 +7515,7 @@
if (parent) {
Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
}
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_CAMERA_DISABLED);
final int userHandle = caller.getUserId();
synchronized (getLockObject()) {
@@ -8589,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;
}
@@ -9205,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();
@@ -9803,8 +9860,14 @@
final long id = mInjector.binderClearCallingIdentity();
try {
- manageUserUnchecked(admin, profileOwner, userHandle, adminExtras,
- /* showDisclaimer= */ true);
+ if (!mInjector.userManagerIsHeadlessSystemUserMode()) {
+ manageUserUnchecked(admin, profileOwner, userHandle, adminExtras,
+ /* showDisclaimer= */ true);
+ } else if (VERBOSE_LOG) {
+ Slog.v(LOG_TAG, "createAndManageUser(): skipping manageUserUnchecked() on user "
+ + userHandle + " on headless system user as it will be called by "
+ + "handleNewUserCreated()");
+ }
if ((flags & DevicePolicyManager.SKIP_SETUP_WIZARD) != 0) {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
@@ -9860,12 +9923,24 @@
}
private void handleNewUserCreated(UserInfo user) {
- if (!mOwners.hasDeviceOwner()) return;
+ if (VERBOSE_LOG) Slog.v(LOG_TAG, "handleNewUserCreated(): " + user.toFullString());
+
+ if (!mOwners.hasDeviceOwner() || !user.isFull() || user.isManagedProfile()) return;
final int userId = user.id;
- Log.i(LOG_TAG, "User " + userId + " added on DO mode; setting ShowNewUserDisclaimer");
- setShowNewUserDisclaimer(userId, DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED);
+ // TODO(b/177547285): add CTS test
+ if (mInjector.userManagerIsHeadlessSystemUserMode()) {
+ ComponentName admin = mOwners.getDeviceOwnerComponent();
+ Slog.i(LOG_TAG, "Automatically setting profile owner (" + admin + ") on new user "
+ + userId);
+ manageUserUnchecked(/* deviceOwner= */ admin, /* profileOwner= */ admin,
+ /* managedUser= */ userId, /* adminExtras= */ null,
+ /* showDisclaimer= */ true);
+ } else {
+ Log.i(LOG_TAG, "User " + userId + " added on DO mode; setting ShowNewUserDisclaimer");
+ setShowNewUserDisclaimer(userId, DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED);
+ }
}
@Override
@@ -11362,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);
@@ -12000,8 +12076,13 @@
}
@Override
- public ComponentName getProfileOwnerAsUser(int userHandle) {
- return DevicePolicyManagerService.this.getProfileOwnerAsUser(userHandle);
+ public ComponentName getProfileOwnerAsUser(@UserIdInt int userId) {
+ return DevicePolicyManagerService.this.getProfileOwnerAsUser(userId);
+ }
+
+ @Override
+ public int getDeviceOwnerUserId() {
+ return DevicePolicyManagerService.this.getDeviceOwnerUserId();
}
@Override
@@ -12333,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());
@@ -12368,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();
@@ -14344,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 {
@@ -14375,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();
@@ -14742,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);
}
@@ -14843,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:
@@ -14915,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/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
index 564950b..457255b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
@@ -52,14 +52,17 @@
/**
* Factory reset the device according to the builder's arguments.
+ *
+ * @return {@code true} if device was factory reset, or {@code false} if it was delayed by the
+ * {@link DevicePolicySafetyChecker}.
*/
- public void factoryReset() throws IOException {
+ public boolean factoryReset() throws IOException {
Preconditions.checkCallAuthorization(mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MASTER_CLEAR) == PackageManager.PERMISSION_GRANTED);
if (mSafetyChecker == null) {
factoryResetInternalUnchecked();
- return;
+ return true;
}
IResultReceiver receiver = new IResultReceiver.Stub() {
@@ -77,6 +80,36 @@
};
Slog.i(TAG, String.format("Delaying factory reset until %s confirms", mSafetyChecker));
mSafetyChecker.onFactoryReset(receiver);
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("FactoryResetter[");
+ if (mReason == null) {
+ builder.append("no_reason");
+ } else {
+ builder.append("reason='").append(mReason).append("'");
+ }
+ if (mSafetyChecker != null) {
+ builder.append(",hasSafetyChecker");
+ }
+ if (mShutdown) {
+ builder.append(",shutdown");
+ }
+ if (mForce) {
+ builder.append(",force");
+ }
+ if (mWipeEuicc) {
+ builder.append(",wipeEuicc");
+ }
+ if (mWipeAdoptableStorage) {
+ builder.append(",wipeAdoptableStorage");
+ }
+ if (mWipeFactoryResetProtection) {
+ builder.append(",ipeFactoryResetProtection");
+ }
+ return builder.append(']').toString();
}
private void factoryResetInternalUnchecked() throws IOException {
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/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 16b9165..091e688 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -188,6 +188,9 @@
ConversationStatus status) {
handleIncomingUser(userId);
checkCallerIsSameApp(packageName);
+ if (status.getStartTimeMillis() > System.currentTimeMillis()) {
+ throw new IllegalArgumentException("Start time must be in the past");
+ }
mDataManager.addOrUpdateStatus(packageName, userId, conversationId, status);
}
diff --git a/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java b/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java
index 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/ConversationStatusExpirationBroadcastReceiver.java b/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java
new file mode 100644
index 0000000..c631026
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.app.people.ConversationStatus;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.CancellationSignal;
+import android.os.SystemClock;
+
+import com.android.server.LocalServices;
+import com.android.server.notification.NotificationRecord;
+import com.android.server.people.PeopleServiceInternal;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * If a {@link ConversationStatus} is added to the system with an expiration time, remove that
+ * status at that time
+ */
+public class ConversationStatusExpirationBroadcastReceiver extends BroadcastReceiver {
+
+ static final String ACTION = "ConversationStatusExpiration";
+ static final String EXTRA_USER_ID = "userId";
+ static final int REQUEST_CODE = 10;
+ static final String SCHEME = "expStatus";
+
+ void scheduleExpiration(Context context, @UserIdInt int userId, String pkg,
+ String conversationId, ConversationStatus status) {
+
+ final PendingIntent pi = PendingIntent.getBroadcast(context,
+ REQUEST_CODE,
+ new Intent(ACTION)
+ .setData(new Uri.Builder().scheme(SCHEME)
+ .appendPath(getKey(userId, pkg, conversationId, status))
+ .build())
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ .putExtra(EXTRA_USER_ID, userId),
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ context.getSystemService(AlarmManager.class).setExactAndAllowWhileIdle(
+ AlarmManager.RTC_WAKEUP, status.getEndTimeMillis(), pi);
+ }
+
+ private static String getKey(@UserIdInt int userId, String pkg,
+ String conversationId, ConversationStatus status) {
+ return userId + pkg + conversationId + status.getId();
+ }
+
+ static IntentFilter getFilter() {
+ IntentFilter conversationStatusFilter =
+ new IntentFilter(ConversationStatusExpirationBroadcastReceiver.ACTION);
+ conversationStatusFilter.addDataScheme(
+ ConversationStatusExpirationBroadcastReceiver.SCHEME);
+ return conversationStatusFilter;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+ if (ACTION.equals(action)) {
+ new Thread(() -> {
+ PeopleServiceInternal peopleServiceInternal =
+ LocalServices.getService(PeopleServiceInternal.class);
+ peopleServiceInternal.pruneDataForUser(intent.getIntExtra(EXTRA_USER_ID,
+ ActivityManager.getCurrentUser()), new CancellationSignal());
+ }).start();
+ }
+ }
+}
diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java
index 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/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index e04e287..7521415 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -124,6 +124,7 @@
private PackageManagerInternal mPackageManagerInternal;
private NotificationManagerInternal mNotificationManagerInternal;
private UserManager mUserManager;
+ private ConversationStatusExpirationBroadcastReceiver mStatusExpReceiver;
public DataManager(Context context) {
this(context, new Injector());
@@ -145,6 +146,10 @@
mShortcutServiceInternal.addShortcutChangeCallback(new ShortcutServiceCallback());
+ mStatusExpReceiver = new ConversationStatusExpirationBroadcastReceiver();
+ mContext.registerReceiver(mStatusExpReceiver,
+ ConversationStatusExpirationBroadcastReceiver.getFilter());
+
IntentFilter shutdownIntentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
BroadcastReceiver shutdownBroadcastReceiver = new ShutdownBroadcastReceiver();
mContext.registerReceiver(shutdownBroadcastReceiver, shutdownIntentFilter);
@@ -296,6 +301,27 @@
}
/**
+ * Removes any status with an expiration time in the past.
+ */
+ public void pruneExpiredConversationStatuses(@UserIdInt int callingUserId, long currentTimeMs) {
+ forPackagesInProfile(callingUserId, packageData -> {
+ final ConversationStore cs = packageData.getConversationStore();
+ packageData.forAllConversations(conversationInfo -> {
+ ConversationInfo.Builder builder = new ConversationInfo.Builder(conversationInfo);
+ List<ConversationStatus> newStatuses = new ArrayList<>();
+ for (ConversationStatus status : conversationInfo.getStatuses()) {
+ if (status.getEndTimeMillis() < 0
+ || currentTimeMs < status.getEndTimeMillis()) {
+ newStatuses.add(status);
+ }
+ }
+ builder.setStatuses(newStatuses);
+ cs.addOrUpdate(builder.build());
+ });
+ });
+ }
+
+ /**
* Returns the last notification interaction with the specified conversation. If the
* conversation can't be found or no interactions have been recorded, returns 0L.
*/
@@ -317,6 +343,12 @@
ConversationInfo.Builder builder = new ConversationInfo.Builder(convToModify);
builder.addOrUpdateStatus(status);
cs.addOrUpdate(builder.build());
+
+ if (status.getEndTimeMillis() >= 0) {
+ mStatusExpReceiver.scheduleExpiration(
+ mContext, userId, packageName, conversationId, status);
+ }
+
}
public void clearStatus(String packageName, int userId, String conversationId,
@@ -458,6 +490,7 @@
packageData.getEventStore().deleteEventHistories(EventStore.CATEGORY_SMS);
}
packageData.pruneOrphanEvents();
+ pruneExpiredConversationStatuses(userId, System.currentTimeMillis());
pruneOldRecentConversations(userId, System.currentTimeMillis());
cleanupCachedShortcuts(userId, MAX_CACHED_RECENT_SHORTCUTS);
});
diff --git a/services/searchui/OWNERS b/services/searchui/OWNERS
new file mode 100644
index 0000000..92835c2
--- /dev/null
+++ b/services/searchui/OWNERS
@@ -0,0 +1,2 @@
+hyunyoungs@google.com
+sfufa@google.com
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiverTest.kt b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiverTest.kt
index 6de3d4e..23ed278 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiverTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiverTest.kt
@@ -77,12 +77,14 @@
val filter = IntentFilter().apply {
addAction(Intent.ACTION_VIEW)
addCategory(Intent.CATEGORY_DEFAULT)
+ addCategory(Intent.CATEGORY_BROWSABLE)
addDataScheme(uri.scheme)
addDataAuthority(uri.authority, null)
}
val intent = Intent(Intent.ACTION_VIEW, uri).apply {
addCategory(Intent.CATEGORY_DEFAULT)
+ addCategory(Intent.CATEGORY_BROWSABLE)
}
val allResults = context.packageManager.queryIntentActivities(intent, 0)
val allComponents = allResults
@@ -132,6 +134,8 @@
val intent = Intent(Intent.ACTION_VIEW).apply {
data = uri
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addCategory(Intent.CATEGORY_BROWSABLE)
}
val expectedActivities = params.expected.toMutableList()
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/devicepolicy/FactoryResetterTest.java b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java
index c4d14f9..589a349 100644
--- a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java
@@ -20,6 +20,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.never;
@@ -158,10 +160,11 @@
public void testFactoryReset_storageOnly() throws Exception {
allowFactoryReset();
- FactoryResetter.newBuilder(mContext)
+ boolean success = FactoryResetter.newBuilder(mContext)
.setWipeAdoptableStorage(true).build()
.factoryReset();
+ assertThat(success).isTrue();
verifyWipeAdoptableStorageCalled();
verifyWipeFactoryResetProtectionNotCalled();
verifyRebootWipeUserDataMinimumArgsCalled();
@@ -171,10 +174,11 @@
public void testFactoryReset_frpOnly() throws Exception {
allowFactoryReset();
- FactoryResetter.newBuilder(mContext)
+ boolean success = FactoryResetter.newBuilder(mContext)
.setWipeFactoryResetProtection(true)
.build().factoryReset();
+ assertThat(success).isTrue();
verifyWipeAdoptableStorageNotCalled();
verifyWipeFactoryResetProtectionCalled();
verifyRebootWipeUserDataMinimumArgsCalled();
@@ -184,7 +188,7 @@
public void testFactoryReset_allArgs() throws Exception {
allowFactoryReset();
- FactoryResetter.newBuilder(mContext)
+ boolean success = FactoryResetter.newBuilder(mContext)
.setReason(REASON)
.setForce(true)
.setShutdown(true)
@@ -193,6 +197,7 @@
.setWipeFactoryResetProtection(true)
.build().factoryReset();
+ assertThat(success).isTrue();
verifyWipeAdoptableStorageCalled();
verifyWipeFactoryResetProtectionCalled();
verifyRebootWipeUserDataAllArgsCalled();
@@ -202,9 +207,10 @@
public void testFactoryReset_minimumArgs_safetyChecker_neverReplied() throws Exception {
allowFactoryReset();
- FactoryResetter.newBuilder(mContext).setSafetyChecker(mSafetyChecker).build()
- .factoryReset();
+ boolean success = FactoryResetter.newBuilder(mContext)
+ .setSafetyChecker(mSafetyChecker).build().factoryReset();
+ assertThat(success).isFalse();
verifyWipeAdoptableStorageNotCalled();
verifyWipeFactoryResetProtectionNotCalled();
verifyRebootWipeUserDataNotCalled();
@@ -221,7 +227,7 @@
return null;
}).when(mSafetyChecker).onFactoryReset(any());
- FactoryResetter.newBuilder(mContext)
+ boolean success = FactoryResetter.newBuilder(mContext)
.setSafetyChecker(mSafetyChecker)
.setReason(REASON)
.setForce(true)
@@ -231,6 +237,7 @@
.setWipeFactoryResetProtection(true)
.build().factoryReset();
+ assertThat(success).isFalse();
verifyWipeAdoptableStorageCalled();
verifyWipeFactoryResetProtectionCalled();
verifyRebootWipeUserDataAllArgsCalled();
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/location/gnss/hal/FakeGnssHal.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
index 675274b..69fe140 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
@@ -233,6 +233,7 @@
private boolean mAntennaInfoListeningStarted = false;
private boolean mMeasurementCollectionStarted = false;
private boolean mMeasurementCollectionFullTracking = false;
+ private boolean mMeasurementCollectionCorrVecOutputsEnabled = false;
private GnssHalPositionMode mPositionMode = new GnssHalPositionMode();
private GnssHalBatchingMode mBatchingMode = new GnssHalBatchingMode();
private final ArrayList<Location> mBatchedLocations = new ArrayList<>();
@@ -521,9 +522,11 @@
}
@Override
- protected boolean startMeasurementCollection(boolean enableFullTracking) {
+ protected boolean startMeasurementCollection(boolean enableFullTracking,
+ boolean enableCorrVecOutputs) {
mState.mMeasurementCollectionStarted = true;
mState.mMeasurementCollectionFullTracking = enableFullTracking;
+ mState.mMeasurementCollectionCorrVecOutputsEnabled = enableCorrVecOutputs;
return true;
}
@@ -531,6 +534,7 @@
protected boolean stopMeasurementCollection() {
mState.mMeasurementCollectionStarted = false;
mState.mMeasurementCollectionFullTracking = false;
+ mState.mMeasurementCollectionCorrVecOutputsEnabled = false;
return true;
}
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 c6fde87..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
@@ -22,13 +22,13 @@
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.GenericDocument;
+import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.SearchResult;
import android.app.appsearch.SearchResultPage;
import android.app.appsearch.SearchSpec;
import android.app.appsearch.exceptions.AppSearchException;
import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter;
-import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter;
import com.android.server.appsearch.proto.DocumentProto;
import com.android.server.appsearch.proto.GetOptimizeInfoResultProto;
import com.android.server.appsearch.proto.PropertyConfigProto;
@@ -41,6 +41,7 @@
import com.android.server.appsearch.proto.TermMatchType;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.junit.Before;
@@ -55,27 +56,10 @@
public class AppSearchImplTest {
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
private AppSearchImpl mAppSearchImpl;
- private SchemaTypeConfigProto mVisibilitySchemaProto;
@Before
public void setUp() throws Exception {
mAppSearchImpl = AppSearchImpl.create(mTemporaryFolder.newFolder());
-
- AppSearchSchema visibilitySchema = VisibilityStore.SCHEMA;
-
- // We need to rewrite the schema type to follow AppSearchImpl's prefixing scheme.
- AppSearchSchema.Builder rewrittenVisibilitySchema =
- new AppSearchSchema.Builder(
- AppSearchImpl.createPrefix(
- VisibilityStore.PACKAGE_NAME, VisibilityStore.DATABASE_NAME)
- + VisibilityStore.SCHEMA_TYPE);
- List<AppSearchSchema.PropertyConfig> visibilityProperties =
- visibilitySchema.getProperties();
- for (AppSearchSchema.PropertyConfig property : visibilityProperties) {
- rewrittenVisibilitySchema.addProperty(property);
- }
- mVisibilitySchemaProto =
- SchemaToProtoConverter.toSchemaTypeConfigProto(rewrittenVisibilitySchema.build());
}
// TODO(b/175430168) add test to verify reset is working properly.
@@ -407,6 +391,7 @@
"database",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
// Insert enough documents.
@@ -464,6 +449,7 @@
"database",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
// Insert document
@@ -495,12 +481,14 @@
"database1",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
mAppSearchImpl.setSchema(
"package",
"database2",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
// Insert documents
@@ -537,6 +525,117 @@
assertThat(searchResultPage.getResults()).isEmpty();
}
+ /**
+ * TODO(b/169883602): This should be an integration test at the cts-level. This is a short-term
+ * test until we have official support for multiple-apps indexing at once.
+ */
+ @Test
+ public void testQueryWithMultiplePackages_noPackageFilters() throws Exception {
+ // Insert package1 schema
+ List<AppSearchSchema> schema1 =
+ ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
+ mAppSearchImpl.setSchema(
+ "package1",
+ "database1",
+ schema1,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false);
+
+ // Insert package2 schema
+ List<AppSearchSchema> schema2 =
+ ImmutableList.of(new AppSearchSchema.Builder("schema2").build());
+ mAppSearchImpl.setSchema(
+ "package2",
+ "database2",
+ schema2,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false);
+
+ // Insert package1 document
+ GenericDocument document =
+ new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
+ mAppSearchImpl.putDocument("package1", "database1", document);
+
+ // No query filters specified, package2 shouldn't be able to query for package1's documents.
+ SearchSpec searchSpec =
+ new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
+ SearchResultPage searchResultPage =
+ mAppSearchImpl.query("package2", "database2", "", searchSpec);
+ assertThat(searchResultPage.getResults()).isEmpty();
+
+ // Insert package2 document
+ document =
+ new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
+ mAppSearchImpl.putDocument("package2", "database2", document);
+
+ // No query filters specified. package2 should only get its own documents back.
+ searchResultPage = mAppSearchImpl.query("package2", "database2", "", searchSpec);
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document);
+ }
+
+ /**
+ * TODO(b/169883602): This should be an integration test at the cts-level. This is a short-term
+ * test until we have official support for multiple-apps indexing at once.
+ */
+ @Test
+ public void testQueryWithMultiplePackages_withPackageFilters() throws Exception {
+ // Insert package1 schema
+ List<AppSearchSchema> schema1 =
+ ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
+ mAppSearchImpl.setSchema(
+ "package1",
+ "database1",
+ schema1,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false);
+
+ // Insert package2 schema
+ List<AppSearchSchema> schema2 =
+ ImmutableList.of(new AppSearchSchema.Builder("schema2").build());
+ mAppSearchImpl.setSchema(
+ "package2",
+ "database2",
+ schema2,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false);
+
+ // Insert package1 document
+ GenericDocument document =
+ new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
+ mAppSearchImpl.putDocument("package1", "database1", document);
+
+ // "package1" filter specified, but package2 shouldn't be able to query for package1's
+ // documents.
+ SearchSpec searchSpec =
+ new SearchSpec.Builder()
+ .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+ .addFilterPackageNames("package1")
+ .build();
+ SearchResultPage searchResultPage =
+ mAppSearchImpl.query("package2", "database2", "", searchSpec);
+ assertThat(searchResultPage.getResults()).isEmpty();
+
+ // Insert package2 document
+ document =
+ new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
+ mAppSearchImpl.putDocument("package2", "database2", document);
+
+ // "package2" filter specified, package2 should only get its own documents back.
+ searchSpec =
+ new SearchSpec.Builder()
+ .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+ .addFilterPackageNames("package2")
+ .build();
+ searchResultPage = mAppSearchImpl.query("package2", "database2", "", searchSpec);
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document);
+ }
+
@Test
public void testGlobalQueryEmptyDatabase() throws Exception {
SearchSpec searchSpec =
@@ -545,6 +644,115 @@
assertThat(searchResultPage.getResults()).isEmpty();
}
+ /**
+ * TODO(b/169883602): This should be an integration test at the cts-level. This is a short-term
+ * test until we have official support for multiple-apps indexing at once.
+ */
+ @Test
+ public void testGlobalQueryWithMultiplePackages_noPackageFilters() throws Exception {
+ // Insert package1 schema
+ List<AppSearchSchema> schema1 =
+ ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
+ mAppSearchImpl.setSchema(
+ "package1",
+ "database1",
+ schema1,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false);
+
+ // Insert package2 schema
+ List<AppSearchSchema> schema2 =
+ ImmutableList.of(new AppSearchSchema.Builder("schema2").build());
+ mAppSearchImpl.setSchema(
+ "package2",
+ "database2",
+ schema2,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false);
+
+ // Insert package1 document
+ GenericDocument document1 =
+ new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
+ mAppSearchImpl.putDocument("package1", "database1", document1);
+
+ // Insert package2 document
+ GenericDocument document2 =
+ new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
+ mAppSearchImpl.putDocument("package2", "database2", document2);
+
+ // No query filters specified, global query can retrieve all documents.
+ SearchSpec searchSpec =
+ new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
+ SearchResultPage searchResultPage = mAppSearchImpl.globalQuery("", searchSpec);
+ assertThat(searchResultPage.getResults()).hasSize(2);
+
+ // Document2 will be first since it got indexed later and has a "better", aka more recent
+ // score.
+ assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document2);
+ assertThat(searchResultPage.getResults().get(1).getDocument()).isEqualTo(document1);
+ }
+
+ /**
+ * TODO(b/169883602): This should be an integration test at the cts-level. This is a short-term
+ * test until we have official support for multiple-apps indexing at once.
+ */
+ @Test
+ public void testGlobalQueryWithMultiplePackages_withPackageFilters() throws Exception {
+ // Insert package1 schema
+ List<AppSearchSchema> schema1 =
+ ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
+ mAppSearchImpl.setSchema(
+ "package1",
+ "database1",
+ schema1,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false);
+
+ // Insert package2 schema
+ List<AppSearchSchema> schema2 =
+ ImmutableList.of(new AppSearchSchema.Builder("schema2").build());
+ mAppSearchImpl.setSchema(
+ "package2",
+ "database2",
+ schema2,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false);
+
+ // Insert package1 document
+ GenericDocument document1 =
+ new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
+ mAppSearchImpl.putDocument("package1", "database1", document1);
+
+ // Insert package2 document
+ GenericDocument document2 =
+ new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
+ mAppSearchImpl.putDocument("package2", "database2", document2);
+
+ // "package1" filter specified
+ SearchSpec searchSpec =
+ new SearchSpec.Builder()
+ .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+ .addFilterPackageNames("package1")
+ .build();
+ SearchResultPage searchResultPage = mAppSearchImpl.globalQuery("", searchSpec);
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document1);
+
+ // "package2" filter specified
+ searchSpec =
+ new SearchSpec.Builder()
+ .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+ .addFilterPackageNames("package2")
+ .build();
+ searchResultPage = mAppSearchImpl.globalQuery("", searchSpec);
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document2);
+ }
+
@Test
public void testRemoveEmptyDatabase_noExceptionThrown() throws Exception {
SearchSpec searchSpec =
@@ -567,6 +775,9 @@
@Test
public void testSetSchema() throws Exception {
+ List<SchemaTypeConfigProto> existingSchemas =
+ mAppSearchImpl.getSchemaProtoLocked().getTypesList();
+
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("Email").build());
// Set schema Email to AppSearch database1
@@ -575,6 +786,7 @@
"database1",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
// Create expected schemaType proto.
@@ -586,7 +798,7 @@
.build();
List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
- expectedTypes.add(mVisibilitySchemaProto);
+ expectedTypes.addAll(existingSchemas);
expectedTypes.addAll(expectedProto.getTypesList());
assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
.containsExactlyElementsIn(expectedTypes);
@@ -594,20 +806,30 @@
@Test
public void testSetSchema_existingSchemaRetainsVisibilitySetting() throws Exception {
+ PackageIdentifier package1 =
+ new PackageIdentifier("package1", /*sha256Certificate=*/ new byte[] {100});
+
String prefix = AppSearchImpl.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
+ /*schemasPackageAccessible=*/ ImmutableMap.of(
+ "schema1", ImmutableList.of(package1)),
/*forceOverride=*/ false);
- // "schema1" is platform hidden now
+ // "schema1" is platform hidden now and package visible to package1
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
.isFalse();
+ assertThat(
+ mAppSearchImpl
+ .getVisibilityStoreLocked()
+ .isSchemaPackageAccessible(prefix, prefix + "schema1", package1))
+ .isTrue();
// Add a new schema, and include the already-existing "schema1"
mAppSearchImpl.setSchema(
@@ -617,10 +839,11 @@
new AppSearchSchema.Builder("schema1").build(),
new AppSearchSchema.Builder("schema2").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
+ /*schemasPackageAccessible=*/ ImmutableMap.of(
+ "schema1", ImmutableList.of(package1)),
/*forceOverride=*/ false);
- // Check that "schema1" is still platform hidden, but "schema2" is the default platform
- // visible.
+ // Check that "schema1" still has the same visibility settings
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
@@ -629,12 +852,27 @@
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
+ .isSchemaPackageAccessible(prefix, prefix + "schema1", package1))
+ .isTrue();
+
+ // "schema2" has default visibility settings
+ assertThat(
+ mAppSearchImpl
+ .getVisibilityStoreLocked()
.isSchemaPlatformSurfaceable(prefix, prefix + "schema2"))
.isTrue();
+ assertThat(
+ mAppSearchImpl
+ .getVisibilityStoreLocked()
+ .isSchemaPackageAccessible(prefix, prefix + "schema2", package1))
+ .isFalse();
}
@Test
public void testRemoveSchema() throws Exception {
+ List<SchemaTypeConfigProto> existingSchemas =
+ mAppSearchImpl.getSchemaProtoLocked().getTypesList();
+
List<AppSearchSchema> schemas =
ImmutableList.of(
new AppSearchSchema.Builder("Email").build(),
@@ -645,6 +883,7 @@
"database1",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
// Create expected schemaType proto.
@@ -660,7 +899,7 @@
// Check both schema Email and Document saved correctly.
List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
- expectedTypes.add(mVisibilitySchemaProto);
+ expectedTypes.addAll(existingSchemas);
expectedTypes.addAll(expectedProto.getTypesList());
assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
.containsExactlyElementsIn(expectedTypes);
@@ -677,6 +916,7 @@
"database1",
finalSchemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false));
assertThat(e).hasMessageThat().contains("Schema is incompatible");
assertThat(e).hasMessageThat().contains("Deleted types: [package$database1/Document]");
@@ -687,6 +927,7 @@
"database1",
finalSchemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ true);
// Check Document schema is removed.
@@ -698,7 +939,7 @@
.build();
expectedTypes = new ArrayList<>();
- expectedTypes.add(mVisibilitySchemaProto);
+ expectedTypes.addAll(existingSchemas);
expectedTypes.addAll(expectedProto.getTypesList());
assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
.containsExactlyElementsIn(expectedTypes);
@@ -706,6 +947,9 @@
@Test
public void testRemoveSchema_differentDataBase() throws Exception {
+ List<SchemaTypeConfigProto> existingSchemas =
+ mAppSearchImpl.getSchemaProtoLocked().getTypesList();
+
// Create schemas
List<AppSearchSchema> schemas =
ImmutableList.of(
@@ -718,12 +962,14 @@
"database1",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
mAppSearchImpl.setSchema(
"package",
"database2",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
// Create expected schemaType proto.
@@ -745,7 +991,7 @@
// Check Email and Document is saved in database 1 and 2 correctly.
List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
- expectedTypes.add(mVisibilitySchemaProto);
+ expectedTypes.addAll(existingSchemas);
expectedTypes.addAll(expectedProto.getTypesList());
assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
.containsExactlyElementsIn(expectedTypes);
@@ -757,6 +1003,7 @@
"database1",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ true);
// Create expected schemaType list, database 1 should only contain Email but database 2
@@ -776,7 +1023,7 @@
// Check nothing changed in database2.
expectedTypes = new ArrayList<>();
- expectedTypes.add(mVisibilitySchemaProto);
+ expectedTypes.addAll(existingSchemas);
expectedTypes.addAll(expectedProto.getTypesList());
assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
.containsExactlyElementsIn(expectedTypes);
@@ -784,49 +1031,71 @@
@Test
public void testRemoveSchema_removedFromVisibilityStore() throws Exception {
+ PackageIdentifier package1 =
+ new PackageIdentifier("package1", /*sha256Certificate=*/ new byte[] {100});
+
String prefix = AppSearchImpl.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
+ /*schemasPackageAccessible=*/ ImmutableMap.of(
+ "schema1", ImmutableList.of(package1)),
/*forceOverride=*/ false);
- // "schema1" is platform hidden now
+ // "schema1" is platform hidden now and package accessible
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
.isFalse();
+ assertThat(
+ mAppSearchImpl
+ .getVisibilityStoreLocked()
+ .isSchemaPackageAccessible(prefix, prefix + "schema1", package1))
+ .isTrue();
// Remove "schema1" by force overriding
mAppSearchImpl.setSchema(
"package",
"database",
- Collections.emptyList(),
+ /*schemas=*/ Collections.emptyList(),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ true);
- // Check that "schema1" is no longer considered platform hidden
+ // Check that "schema1" is no longer considered platform hidden or package accessible
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
.isTrue();
+ assertThat(
+ mAppSearchImpl
+ .getVisibilityStoreLocked()
+ .isSchemaPackageAccessible(prefix, prefix + "schema1", package1))
+ .isFalse();
// Add "schema1" back, it gets default visibility settings which means it's not platform
- // hidden.
+ // hidden and not package accessible
mAppSearchImpl.setSchema(
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
assertThat(
mAppSearchImpl
.getVisibilityStoreLocked()
.isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
.isTrue();
+ assertThat(
+ mAppSearchImpl
+ .getVisibilityStoreLocked()
+ .isSchemaPackageAccessible(prefix, prefix + "schema1", package1))
+ .isFalse();
}
@Test
@@ -837,6 +1106,7 @@
"database",
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
assertThat(
mAppSearchImpl
@@ -853,6 +1123,7 @@
"database",
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("Schema"),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
assertThat(
mAppSearchImpl
@@ -862,6 +1133,46 @@
}
@Test
+ public void testSetSchema_defaultNotPackageAccessible() throws Exception {
+ PackageIdentifier package1 =
+ new PackageIdentifier("package1", /*sha256Certificate=*/ new byte[] {100});
+
+ String prefix = AppSearchImpl.createPrefix("package", "database");
+ mAppSearchImpl.setSchema(
+ "package",
+ "database",
+ Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false);
+ assertThat(
+ mAppSearchImpl
+ .getVisibilityStoreLocked()
+ .isSchemaPackageAccessible(prefix, prefix + "Schema", package1))
+ .isFalse();
+ }
+
+ @Test
+ public void testSetSchema_packageAccessible() throws Exception {
+ PackageIdentifier package1 =
+ new PackageIdentifier("package1", /*sha256Certificate=*/ new byte[] {100});
+
+ String prefix = AppSearchImpl.createPrefix("package", "database");
+ mAppSearchImpl.setSchema(
+ "package",
+ "database",
+ Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ ImmutableMap.of("Schema", ImmutableList.of(package1)),
+ /*forceOverride=*/ false);
+ assertThat(
+ mAppSearchImpl
+ .getVisibilityStoreLocked()
+ .isSchemaPackageAccessible(prefix, prefix + "Schema", package1))
+ .isTrue();
+ }
+
+ @Test
public void testHasSchemaType() throws Exception {
// Nothing exists yet
assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "Schema")).isFalse();
@@ -871,6 +1182,7 @@
"database",
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "Schema")).isTrue();
@@ -892,6 +1204,7 @@
"database1",
Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
assertThat(mAppSearchImpl.getPrefixesLocked())
.containsExactly(
@@ -905,6 +1218,7 @@
"database2",
Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false);
assertThat(mAppSearchImpl.getPrefixesLocked())
.containsExactly(
@@ -916,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 =
@@ -943,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/VisibilityStoreTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java
index 415c1f5..e491ac3 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java
@@ -18,6 +18,10 @@
import static com.google.common.truth.Truth.assertThat;
+import android.app.appsearch.PackageIdentifier;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.junit.Before;
@@ -70,11 +74,12 @@
}
@Test
- public void testSetVisibility() throws Exception {
+ public void testSetVisibility_platformSurfaceable() throws Exception {
mVisibilityStore.setVisibility(
"prefix",
/*schemasNotPlatformSurfaceable=*/ ImmutableSet.of(
- "prefix/schema1", "prefix/schema2"));
+ "prefix/schema1", "prefix/schema2"),
+ /*schemasPackageAccessible=*/ Collections.emptyMap());
assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema1"))
.isFalse();
assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema2"))
@@ -85,7 +90,8 @@
mVisibilityStore.setVisibility(
"prefix",
/*schemasNotPlatformSurfaceable=*/ ImmutableSet.of(
- "prefix/schema1", "prefix/schema3"));
+ "prefix/schema1", "prefix/schema3"),
+ /*schemasPackageAccessible=*/ Collections.emptyMap());
assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema1"))
.isFalse();
assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema2"))
@@ -94,7 +100,9 @@
.isFalse();
mVisibilityStore.setVisibility(
- "prefix", /*schemasNotPlatformSurfaceable=*/ Collections.emptySet());
+ "prefix",
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap());
assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema1"))
.isTrue();
assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema2"))
@@ -104,13 +112,72 @@
}
@Test
+ public void testSetVisibility_packageAccessible() throws Exception {
+ PackageIdentifier package1 =
+ new PackageIdentifier("package1", /*sha256Certificate=*/ new byte[] {100});
+ PackageIdentifier package2 =
+ new PackageIdentifier("package2", /*sha256Certificate=*/ new byte[] {100});
+ PackageIdentifier package3 =
+ new PackageIdentifier("package3", /*sha256Certificate=*/ new byte[] {100});
+
+ mVisibilityStore.setVisibility(
+ "prefix",
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
+ /*schemasPackageAccessible=*/ ImmutableMap.of(
+ "prefix/schema1", ImmutableList.of(package1),
+ "prefix/schema2", ImmutableList.of(package2)));
+ assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema1", package1))
+ .isTrue();
+ assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema2", package2))
+ .isTrue();
+
+ // New .setVisibility() call completely overrides previous visibility settings. So
+ // "schema2" isn't preserved.
+ mVisibilityStore.setVisibility(
+ "prefix",
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
+ /*schemasPackageAccessible=*/ ImmutableMap.of(
+ "prefix/schema1", ImmutableList.of(package1),
+ "prefix/schema3", ImmutableList.of(package3)));
+ assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema1", package1))
+ .isTrue();
+ assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema2", package2))
+ .isFalse();
+ assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema3", package3))
+ .isTrue();
+
+ mVisibilityStore.setVisibility(
+ "prefix",
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap());
+ assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema1", package1))
+ .isFalse();
+ assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema2", package2))
+ .isFalse();
+ assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema3", package3))
+ .isFalse();
+ }
+
+ @Test
public void testEmptyPrefix() throws Exception {
+ PackageIdentifier package1 =
+ new PackageIdentifier("package1", /*sha256Certificate=*/ new byte[] {100});
+ PackageIdentifier package2 =
+ new PackageIdentifier("package2", /*sha256Certificate=*/ new byte[] {100});
+
mVisibilityStore.setVisibility(
/*prefix=*/ "",
- /*schemasNotPlatformSurfaceable=*/ ImmutableSet.of("schema1", "schema2"));
+ /*schemasNotPlatformSurfaceable=*/ ImmutableSet.of("schema1", "schema2"),
+ /*schemasPackageAccessible=*/ ImmutableMap.of(
+ "schema1", ImmutableList.of(package1),
+ "schema2", ImmutableList.of(package2)));
assertThat(mVisibilityStore.isSchemaPlatformSurfaceable(/*prefix=*/ "", "schema1"))
.isFalse();
assertThat(mVisibilityStore.isSchemaPlatformSurfaceable(/*prefix=*/ "", "schema2"))
.isFalse();
+ assertThat(mVisibilityStore.isSchemaPackageAccessible(/*prefix=*/ "", "schema1", package1))
+ .isTrue();
+ assertThat(mVisibilityStore.isSchemaPackageAccessible(/*prefix=*/ "", "schema2", package2))
+ .isTrue();
}
}
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 24e7d7d..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,10 +71,12 @@
@Test
public void testClientDuplicateFinish_ignoredBySchedulerAndDoesNotCrash() {
- final ClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class);
+ final HalClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class);
- final ClientMonitor<Object> client1 = new TestClientMonitor(mContext, mToken, nonNullDaemon);
- final ClientMonitor<Object> client2 = new TestClientMonitor(mContext, mToken, nonNullDaemon);
+ final HalClientMonitor<Object> client1 =
+ new TestClientMonitor(mContext, mToken, nonNullDaemon);
+ final HalClientMonitor<Object> client2 =
+ new TestClientMonitor(mContext, mToken, nonNullDaemon);
mScheduler.scheduleClientMonitor(client1);
mScheduler.scheduleClientMonitor(client2);
@@ -87,19 +89,19 @@
// Even if second client has a non-null daemon, it needs to be canceled.
Object daemon2 = mock(Object.class);
- final ClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null;
- final ClientMonitor.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);
- final ClientMonitor.Callback callback1 = mock(ClientMonitor.Callback.class);
- final ClientMonitor.Callback callback2 = mock(ClientMonitor.Callback.class);
+ final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
+ final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
// Pretend the scheduler is busy so the first operation doesn't start right away. We want
// to pretend like there are two operations in the queue before kicking things off
mScheduler.mCurrentOperation = new BiometricScheduler.Operation(
- mock(ClientMonitor.class), mock(ClientMonitor.Callback.class));
+ mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class));
mScheduler.scheduleClientMonitor(client1, callback1);
assertEquals(1, mScheduler.mPendingOperations.size());
@@ -124,8 +126,8 @@
// Second non-BiometricPrompt client has a valid daemon
final Object daemon2 = mock(Object.class);
- final ClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null;
- final ClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2;
+ final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null;
+ final HalClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2;
final ClientMonitorCallbackConverter listener1 = mock(ClientMonitorCallbackConverter.class);
@@ -133,13 +135,13 @@
new BiometricPromptClientMonitor(mContext, mToken, lazyDaemon1, listener1);
final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2);
- final ClientMonitor.Callback callback1 = mock(ClientMonitor.Callback.class);
- final ClientMonitor.Callback callback2 = mock(ClientMonitor.Callback.class);
+ final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
+ final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
// Pretend the scheduler is busy so the first operation doesn't start right away. We want
// to pretend like there are two operations in the queue before kicking things off
mScheduler.mCurrentOperation = new BiometricScheduler.Operation(
- mock(ClientMonitor.class), mock(ClientMonitor.Callback.class));
+ mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class));
mScheduler.scheduleClientMonitor(client1, callback1);
assertEquals(1, mScheduler.mPendingOperations.size());
@@ -165,16 +167,16 @@
@Test
public void testCancelNotInvoked_whenOperationWaitingForCookie() {
- final ClientMonitor.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 ClientMonitor.Callback callback1 = mock(ClientMonitor.Callback.class);
+ final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
// Schedule a BiometricPrompt authentication request
mScheduler.scheduleClientMonitor(client1, callback1);
- assertEquals(Operation.STATE_WAITING_FOR_COOKIE, mScheduler.mCurrentOperation.state);
- assertEquals(client1, mScheduler.mCurrentOperation.clientMonitor);
+ assertEquals(Operation.STATE_WAITING_FOR_COOKIE, mScheduler.mCurrentOperation.mState);
+ assertEquals(client1, mScheduler.mCurrentOperation.mClientMonitor);
assertEquals(0, mScheduler.mPendingOperations.size());
// Request it to be canceled. The operation can be canceled immediately, and the scheduler
@@ -205,7 +207,7 @@
}
}
- private static class TestClientMonitor extends ClientMonitor<Object> {
+ private static class TestClientMonitor extends HalClientMonitor<Object> {
private boolean mUnableToStart;
private boolean mStarted;
@@ -221,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 efdbda3..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
@@ -31,8 +31,9 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
-import com.android.server.biometrics.sensors.ClientMonitor;
+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 ClientMonitor testMonitor = mock(ClientMonitor.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 624775b..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
@@ -31,8 +31,9 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
-import com.android.server.biometrics.sensors.ClientMonitor;
+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 ClientMonitor testMonitor = mock(ClientMonitor.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/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 9c28c99..f3576af 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -319,10 +319,10 @@
}
@Override
- void recoverySystemRebootWipeUserData(boolean shutdown, String reason, boolean force,
+ boolean recoverySystemRebootWipeUserData(boolean shutdown, String reason, boolean force,
boolean wipeEuicc, boolean wipeExtRequested, boolean wipeResetProtectionData)
throws IOException {
- services.recoverySystem.rebootWipeUserData(shutdown, reason, force, wipeEuicc,
+ return services.recoverySystem.rebootWipeUserData(shutdown, reason, force, wipeEuicc,
wipeExtRequested, wipeResetProtectionData);
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 7d1de86..d8e0c5c 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -392,9 +392,10 @@
}
public static class RecoverySystemForMock {
- public void rebootWipeUserData(boolean shutdown, String reason, boolean force,
+ public boolean rebootWipeUserData(boolean shutdown, String reason, boolean force,
boolean wipeEuicc, boolean wipeExtRequested, boolean wipeResetProtectionData)
throws IOException {
+ return false;
}
}
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/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index be8a99c..63330d5 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -42,12 +42,14 @@
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -141,6 +143,7 @@
@Mock private JobScheduler mJobScheduler;
@Mock private StatusBarNotification mStatusBarNotification;
@Mock private Notification mNotification;
+ @Mock private AlarmManager mAlarmManager;
@Captor private ArgumentCaptor<ShortcutChangeCallback> mShortcutChangeCallbackCaptor;
@Captor private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
@@ -152,7 +155,6 @@
private DataManager mDataManager;
private CancellationSignal mCancellationSignal;
private ShortcutChangeCallback mShortcutChangeCallback;
- private BroadcastReceiver mShutdownBroadcastReceiver;
private ShortcutInfo mShortcutInfo;
private TestInjector mInjector;
@@ -187,10 +189,15 @@
Context originalContext = getInstrumentation().getTargetContext();
when(mContext.getApplicationInfo()).thenReturn(originalContext.getApplicationInfo());
+ when(mContext.getUser()).thenReturn(originalContext.getUser());
+ when(mContext.getPackageName()).thenReturn(originalContext.getPackageName());
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
when(mContext.getSystemServiceName(UserManager.class)).thenReturn(
Context.USER_SERVICE);
+ when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
+ when(mContext.getSystemServiceName(AlarmManager.class)).thenReturn(
+ Context.ALARM_SERVICE);
when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
@@ -246,8 +253,7 @@
mShortcutChangeCallbackCaptor.capture());
mShortcutChangeCallback = mShortcutChangeCallbackCaptor.getValue();
- verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), any());
- mShutdownBroadcastReceiver = mBroadcastReceiverCaptor.getValue();
+ verify(mContext, times(2)).registerReceiver(any(), any());
}
@After
@@ -767,6 +773,36 @@
}
@Test
+ public void testPruneExpiredConversationStatuses() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ ConversationStatus cs1 = new ConversationStatus.Builder("cs1", 9)
+ .setEndTimeMillis(System.currentTimeMillis())
+ .build();
+ ConversationStatus cs2 = new ConversationStatus.Builder("cs2", 10)
+ .build();
+ ConversationStatus cs3 = new ConversationStatus.Builder("cs3", 1)
+ .setEndTimeMillis(Long.MAX_VALUE)
+ .build();
+ mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs1);
+ mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2);
+ mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs3);
+
+ mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
+
+ assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
+ .doesNotContain(cs1);
+ assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
+ .contains(cs2);
+ assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
+ .contains(cs3);
+ }
+
+ @Test
public void testDoNotUncacheShortcutWithActiveNotifications() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
NotificationListenerService listenerService =
@@ -976,6 +1012,29 @@
.contains(cs);
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
.contains(cs2);
+
+ verify(mAlarmManager, never()).setExactAndAllowWhileIdle(anyInt(), anyLong(), any());
+ }
+
+ @Test
+ public void testAddOrUpdateStatus_schedulesJob() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY)
+ .setEndTimeMillis(1000)
+ .build();
+ mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
+
+ ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME)
+ .setEndTimeMillis(3000)
+ .build();
+ mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2);
+
+ verify(mAlarmManager, times(2)).setExactAndAllowWhileIdle(anyInt(), anyLong(), any());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 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 ecdb8bc..d867987 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -29,6 +29,7 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
@@ -36,7 +37,6 @@
import android.os.Handler;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
@@ -76,26 +76,25 @@
@Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
- // TODO(b/131311651): replace with a FakeVibrator instead.
- @Mock private Vibrator mVibratorMock;
@Mock private VibrationSettings.OnVibratorSettingsChanged mListenerMock;
@Mock private PowerManagerInternal mPowerManagerInternalMock;
private TestLooper mTestLooper;
private ContextWrapper mContextSpy;
private AudioManager mAudioManager;
+ private FakeVibrator mFakeVibrator;
private VibrationSettings mVibrationSettings;
private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
@Before
public void setUp() throws Exception {
mTestLooper = new TestLooper();
+ mFakeVibrator = new FakeVibrator();
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
- when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibratorMock);
- when(mVibratorMock.hasVibrator()).thenReturn(true);
+ when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mFakeVibrator);
doAnswer(invocation -> {
mRegisteredPowerModeListener = invocation.getArgument(0);
return null;
@@ -229,9 +228,23 @@
}
@Test
- public void shouldVibrateForUid_withBackgroundAllowedUsage_returnTrue() throws RemoteException {
+ public void shouldVibrateForUid_withForegroundOnlyUsage_returnsTrueWhInForeground() {
+ assertTrue(mVibrationSettings.shouldVibrateForUid(UID, VibrationAttributes.USAGE_TOUCH));
+
+ mVibrationSettings.mUidObserver.onUidStateChanged(
+ UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
+ assertFalse(mVibrationSettings.shouldVibrateForUid(UID, VibrationAttributes.USAGE_TOUCH));
+ }
+
+ @Test
+ public void shouldVibrateForUid_withBackgroundAllowedUsage_returnTrue() {
+ mVibrationSettings.mUidObserver.onUidStateChanged(
+ UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
+
assertTrue(mVibrationSettings.shouldVibrateForUid(UID, VibrationAttributes.USAGE_ALARM));
assertTrue(mVibrationSettings.shouldVibrateForUid(UID,
+ VibrationAttributes.USAGE_COMMUNICATION_REQUEST));
+ assertTrue(mVibrationSettings.shouldVibrateForUid(UID,
VibrationAttributes.USAGE_NOTIFICATION));
assertTrue(mVibrationSettings.shouldVibrateForUid(UID, VibrationAttributes.USAGE_RINGTONE));
}
@@ -291,12 +304,9 @@
@Test
public void getDefaultIntensity_returnsIntensityFromVibratorService() {
- when(mVibratorMock.getDefaultHapticFeedbackIntensity())
- .thenReturn(Vibrator.VIBRATION_INTENSITY_HIGH);
- when(mVibratorMock.getDefaultNotificationVibrationIntensity())
- .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM);
- when(mVibratorMock.getDefaultRingVibrationIntensity())
- .thenReturn(Vibrator.VIBRATION_INTENSITY_LOW);
+ mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
+ mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
+ mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_OFF);
@@ -322,12 +332,9 @@
@Test
public void getCurrentIntensity_returnsIntensityFromSettings() {
- when(mVibratorMock.getDefaultHapticFeedbackIntensity())
- .thenReturn(Vibrator.VIBRATION_INTENSITY_OFF);
- when(mVibratorMock.getDefaultNotificationVibrationIntensity())
- .thenReturn(Vibrator.VIBRATION_INTENSITY_OFF);
- when(mVibratorMock.getDefaultRingVibrationIntensity())
- .thenReturn(Vibrator.VIBRATION_INTENSITY_OFF);
+ mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_OFF);
+ mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_OFF);
+ mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Vibrator.VIBRATION_INTENSITY_HIGH);
@@ -377,5 +384,4 @@
mAudioManager.setRingerModeInternal(ringerMode);
assertEquals(ringerMode, mAudioManager.getRingerModeInternal());
}
-
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
new file mode 100644
index 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/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index bd622e1..35876e4 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -6531,9 +6531,8 @@
Notification notif = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification();
assertTrue(notif.isBubbleNotification());
- // Our flags should have failed since we're not foreground
+ // The flag should have failed since we're not foreground
assertFalse(notif.getBubbleMetadata().getAutoExpandBubble());
- assertFalse(notif.getBubbleMetadata().isNotificationSuppressed());
}
@Test
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/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index f1dc098..6b69ee9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -351,6 +351,22 @@
}
@Test
+ public void testActivityDrawnBeforeTransition() {
+ mTopActivity.setVisible(false);
+ notifyActivityLaunching(mTopActivity.intent);
+ // Assume the activity is launched the second time consecutively. The drawn event is from
+ // the first time (omitted in test) launch that is earlier than transition.
+ doReturn(true).when(mTopActivity).isReportedDrawn();
+ notifyWindowsDrawn(mTopActivity);
+ notifyActivityLaunched(START_SUCCESS, mTopActivity);
+ // If the launching activity was drawn when starting transition, the launch event should
+ // be reported successfully.
+ notifyTransitionStarting(mTopActivity);
+
+ verifyOnActivityLaunchFinished(mTopActivity);
+ }
+
+ @Test
public void testActivityRecordProtoIsNotTooBig() {
// The ActivityRecordProto must not be too big, otherwise converting it at runtime
// will become prohibitively expensive.
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 83cadf3..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();
@@ -1507,7 +1521,7 @@
final ActivityRecord activity = createActivityWithTask();
final WindowProcessController wpc = activity.app;
assertTrue(wpc.registeredForActivityConfigChanges());
- assertFalse(wpc.registeredForDisplayConfigChanges());
+ assertFalse(wpc.registeredForDisplayAreaConfigChanges());
final ActivityRecord secondaryDisplayActivity =
createActivityOnDisplay(false /* defaultDisplay */, null /* process */);
@@ -1701,7 +1715,7 @@
assertTrue(wpc.registeredForActivityConfigChanges());
assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration()
.diff(wpc.getRequestedOverrideConfiguration()));
- assertFalse(wpc.registeredForDisplayConfigChanges());
+ assertFalse(wpc.registeredForDisplayAreaConfigChanges());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 99ec954..6f5a874 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -43,15 +43,11 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
@@ -60,7 +56,6 @@
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
-import android.window.IDisplayAreaOrganizer;
import com.google.android.collect.Lists;
@@ -503,42 +498,6 @@
assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isEqualTo(tda);
}
- @Test
- public void onParentChanged_onDisplayAreaAppeared() {
- final DisplayArea<WindowContainer> displayArea = new DisplayArea<>(
- mWm, BELOW_TASKS, "NewArea", FEATURE_DEFAULT_TASK_CONTAINER);
-
- final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class);
- spyOn(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController);
- when(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController
- .getOrganizerByFeature(FEATURE_DEFAULT_TASK_CONTAINER))
- .thenReturn(mockDisplayAreaOrganizer);
-
- mDisplayContent.addChild(displayArea, 0);
- assertEquals(mockDisplayAreaOrganizer, displayArea.mOrganizer);
- verify(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController)
- .onDisplayAreaAppeared(
- eq(mockDisplayAreaOrganizer),
- argThat(it -> it == displayArea && it.getSurfaceControl() != null));
- }
-
- @Test
- public void onParentChanged_onDisplayAreaVanished() {
- final DisplayArea<WindowContainer> displayArea = new DisplayArea<>(
- mWm, BELOW_TASKS, "NewArea", FEATURE_DEFAULT_TASK_CONTAINER);
-
- final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class);
- displayArea.mOrganizer = mockDisplayAreaOrganizer;
- spyOn(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController);
-
- mDisplayContent.addChild(displayArea, 0);
- displayArea.removeImmediately();
-
- assertNull(displayArea.mOrganizer);
- verify(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController)
- .onDisplayAreaVanished(mockDisplayAreaOrganizer, displayArea);
- }
-
private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
private TestDisplayArea(WindowManagerService wms, Rect bounds) {
super(wms, ANY, "half display area");
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 69b8ccf..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++) {
@@ -872,6 +891,16 @@
mDisplayContent.getImeTarget(IME_TARGET_LAYERING));
}
+ @UseTestDisplay(addWindows = W_INPUT_METHOD)
+ @Test
+ public void testInputMethodSet_listenOnDisplayAreaConfigurationChanged() {
+ spyOn(mAtm);
+ mDisplayContent.setInputMethodWindowLocked(mImeWindow);
+
+ verify(mAtm).onImeWindowSetOnDisplayArea(
+ mImeWindow.mSession.mPid, mDisplayContent.getImeContainer());
+ }
+
@Test
public void testAllowsTopmostFullscreenOrientation() {
final DisplayContent dc = createNewDisplay();
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/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index 3057558..f848ce5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -20,7 +20,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.view.Display.INVALID_DISPLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -41,6 +40,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import org.junit.Before;
@@ -74,41 +74,69 @@
}
@Test
- public void testDisplayConfigurationListener() {
+ public void testDisplayAreaConfigurationListener() {
+ // By default, the process should not listen to any display area.
+ assertNull(mWpc.getDisplayArea());
- //By default, the process should not listen to any display.
- assertEquals(INVALID_DISPLAY, mWpc.getDisplayId());
+ // Register to ImeContainer on display 1 as a listener.
+ final TestDisplayContent testDisplayContent1 = createTestDisplayContentInContainer();
+ final DisplayArea imeContainer1 = testDisplayContent1.getImeContainer();
+ mWpc.registerDisplayAreaConfigurationListener(imeContainer1);
+ assertTrue(imeContainer1.containsListener(mWpc));
+ assertEquals(imeContainer1, mWpc.getDisplayArea());
- // Register to display 1 as a listener.
- TestDisplayContent testDisplayContent1 = createTestDisplayContentInContainer();
- mWpc.registerDisplayConfigurationListener(testDisplayContent1);
- assertTrue(testDisplayContent1.containsListener(mWpc));
- assertEquals(testDisplayContent1.mDisplayId, mWpc.getDisplayId());
+ // Register to ImeContainer on display 2 as a listener.
+ final TestDisplayContent testDisplayContent2 = createTestDisplayContentInContainer();
+ final DisplayArea imeContainer2 = testDisplayContent2.getImeContainer();
+ mWpc.registerDisplayAreaConfigurationListener(imeContainer2);
+ assertFalse(imeContainer1.containsListener(mWpc));
+ assertTrue(imeContainer2.containsListener(mWpc));
+ assertEquals(imeContainer2, mWpc.getDisplayArea());
- // Move to display 2.
- TestDisplayContent testDisplayContent2 = createTestDisplayContentInContainer();
- mWpc.registerDisplayConfigurationListener(testDisplayContent2);
- assertFalse(testDisplayContent1.containsListener(mWpc));
- assertTrue(testDisplayContent2.containsListener(mWpc));
- assertEquals(testDisplayContent2.mDisplayId, mWpc.getDisplayId());
+ // Null DisplayArea will not change anything.
+ mWpc.registerDisplayAreaConfigurationListener(null);
+ assertTrue(imeContainer2.containsListener(mWpc));
+ assertEquals(imeContainer2, mWpc.getDisplayArea());
- // Null DisplayContent will not change anything.
- mWpc.registerDisplayConfigurationListener(null);
- assertTrue(testDisplayContent2.containsListener(mWpc));
- assertEquals(testDisplayContent2.mDisplayId, mWpc.getDisplayId());
-
- // Unregister listener will remove the wpc from registered displays.
- mWpc.unregisterDisplayConfigurationListener();
- assertFalse(testDisplayContent1.containsListener(mWpc));
- assertFalse(testDisplayContent2.containsListener(mWpc));
- assertEquals(INVALID_DISPLAY, mWpc.getDisplayId());
+ // Unregister listener will remove the wpc from registered display area.
+ mWpc.unregisterDisplayAreaConfigurationListener();
+ assertFalse(imeContainer1.containsListener(mWpc));
+ assertFalse(imeContainer2.containsListener(mWpc));
+ assertNull(mWpc.getDisplayArea());
// Unregistration still work even if the display was removed.
- mWpc.registerDisplayConfigurationListener(testDisplayContent1);
- assertEquals(testDisplayContent1.mDisplayId, mWpc.getDisplayId());
+ mWpc.registerDisplayAreaConfigurationListener(imeContainer1);
+ assertEquals(imeContainer1, mWpc.getDisplayArea());
mRootWindowContainer.removeChild(testDisplayContent1);
- mWpc.unregisterDisplayConfigurationListener();
- assertEquals(INVALID_DISPLAY, mWpc.getDisplayId());
+ mWpc.unregisterDisplayAreaConfigurationListener();
+ assertNull(mWpc.getDisplayArea());
+ }
+
+ @Test
+ public void testDisplayAreaConfigurationListener_verifyConfig() {
+ final Rect displayBounds = new Rect(0, 0, 2000, 1000);
+ final DisplayContent display = new TestDisplayContent.Builder(
+ mAtm, displayBounds.width(), displayBounds.height())
+ .setDensityDpi(300)
+ .setPosition(DisplayContent.POSITION_TOP)
+ .build();
+ final DisplayArea imeContainer = display.getImeContainer();
+
+ // Register to the ime container.
+ mWpc.registerDisplayAreaConfigurationListener(imeContainer);
+
+ assertEquals(displayBounds, mWpc.getConfiguration().windowConfiguration.getBounds());
+
+ // Resize the ime container.
+ final Rect resizeImeBounds = new Rect(0, 0, 1000, 1000);
+ imeContainer.setBounds(resizeImeBounds);
+
+ assertEquals(resizeImeBounds, mWpc.getConfiguration().windowConfiguration.getBounds());
+
+ // Register to the display.
+ mWpc.registerDisplayAreaConfigurationListener(display);
+
+ assertEquals(displayBounds, mWpc.getConfiguration().windowConfiguration.getBounds());
}
@Test
@@ -149,18 +177,19 @@
}
@Test
- public void testConfigurationForSecondaryScreen() {
- // By default, the process should not listen to any display.
- assertEquals(INVALID_DISPLAY, mWpc.getDisplayId());
+ public void testConfigurationForSecondaryScreenDisplayArea() {
+ // By default, the process should not listen to any display area.
+ assertNull(mWpc.getDisplayArea());
- // Register to a new display as a listener.
+ // Register to the ImeContainer on the new display as a listener.
final DisplayContent display = new TestDisplayContent.Builder(mAtm, 2000, 1000)
.setDensityDpi(300).setPosition(DisplayContent.POSITION_TOP).build();
- mWpc.registerDisplayConfigurationListener(display);
+ final DisplayArea imeContainer = display.getImeContainer();
+ mWpc.registerDisplayAreaConfigurationListener(imeContainer);
- assertEquals(display.mDisplayId, mWpc.getDisplayId());
+ assertEquals(imeContainer, mWpc.getDisplayArea());
final Configuration expectedConfig = mAtm.mRootWindowContainer.getConfiguration();
- expectedConfig.updateFrom(display.getConfiguration());
+ expectedConfig.updateFrom(imeContainer.getConfiguration());
assertEquals(expectedConfig, mWpc.getConfiguration());
}
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/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/common/com/android/internal/telephony/SipMessageParsingUtils.java b/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java
new file mode 100644
index 0000000..c7e7cd5
--- /dev/null
+++ b/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.net.Uri;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Utility methods for parsing parts of {@link android.telephony.ims.SipMessage}s.
+ * See RFC 3261 for more information.
+ * @hide
+ */
+// Note: This is lightweight in order to avoid a full SIP stack import in frameworks/base.
+public class SipMessageParsingUtils {
+ private static final String TAG = "SipMessageParsingUtils";
+ // "Method" in request-line
+ // Request-Line = Method SP Request-URI SP SIP-Version CRLF
+ private static final String[] SIP_REQUEST_METHODS = new String[] {"INVITE", "ACK", "OPTIONS",
+ "BYE", "CANCEL", "REGISTER", "PRACK", "SUBSCRIBE", "NOTIFY", "PUBLISH", "INFO", "REFER",
+ "MESSAGE", "UPDATE"};
+
+ // SIP Version 2.0 (corresponding to RCS 3261), set in "SIP-Version" of Status-Line and
+ // Request-Line
+ //
+ // Request-Line = Method SP Request-URI SP SIP-Version CRLF
+ // Status-Line = SIP-Version SP Status-Code SP Reason-Phrase CRLF
+ private static final String SIP_VERSION_2 = "SIP/2.0";
+
+ // headers are formatted Key:Value
+ private static final String HEADER_KEY_VALUE_SEPARATOR = ":";
+ // Multiple of the same header can be concatenated and put into one header Key:Value pair, for
+ // example "v: XX1;branch=YY1,XX2;branch=YY2". This needs to be treated as two "v:" headers.
+ private static final String SUBHEADER_VALUE_SEPARATOR = ",";
+
+ // SIP header parameters have the format ";paramName=paramValue"
+ private static final String PARAM_SEPARATOR = ";";
+ // parameters are formatted paramName=ParamValue
+ private static final String PARAM_KEY_VALUE_SEPARATOR = "=";
+
+ // The via branch parameter definition
+ private static final String BRANCH_PARAM_KEY = "branch";
+
+ // via header key
+ private static final String VIA_SIP_HEADER_KEY = "via";
+ // compact form of the via header key
+ private static final String VIA_SIP_HEADER_KEY_COMPACT = "v";
+
+ /**
+ * @return true if the SIP message start line is considered a request (based on known request
+ * methods).
+ */
+ public static boolean isSipRequest(String startLine) {
+ String[] splitLine = splitStartLineAndVerify(startLine);
+ if (splitLine == null) return false;
+ return verifySipRequest(splitLine);
+ }
+
+ /**
+ * Return the via branch parameter, which is used to identify the transaction ID (request and
+ * response pair) in a SIP transaction.
+ * @param headerString The string containing the headers of the SIP message.
+ */
+ public static String getTransactionId(String headerString) {
+ // search for Via: or v: parameter, we only care about the first one.
+ List<Pair<String, String>> headers = parseHeaders(headerString, true,
+ VIA_SIP_HEADER_KEY, VIA_SIP_HEADER_KEY_COMPACT);
+ for (Pair<String, String> header : headers) {
+ // Headers can also be concatenated together using a "," between each header value.
+ // format becomes v: XX1;branch=YY1,XX2;branch=YY2. Need to extract only the first ID's
+ // branch param YY1.
+ String[] subHeaders = header.second.split(SUBHEADER_VALUE_SEPARATOR);
+ for (String subHeader : subHeaders) {
+ // Search for ;branch=z9hG4bKXXXXXX and return parameter value
+ String[] params = subHeader.split(PARAM_SEPARATOR);
+ if (params.length < 2) {
+ // This param doesn't include a branch param, move to next param.
+ Log.w(TAG, "getTransactionId: via detected without branch param:"
+ + subHeader);
+ continue;
+ }
+ // by spec, each param can only appear once in a header.
+ for (String param : params) {
+ String[] pair = param.split(PARAM_KEY_VALUE_SEPARATOR);
+ if (pair.length < 2) {
+ // ignore info before the first parameter
+ continue;
+ }
+ if (pair.length > 2) {
+ Log.w(TAG,
+ "getTransactionId: unexpected parameter" + Arrays.toString(pair));
+ }
+ // Trim whitespace in parameter
+ pair[0] = pair[0].trim();
+ pair[1] = pair[1].trim();
+ if (BRANCH_PARAM_KEY.equalsIgnoreCase(pair[0])) {
+ // There can be multiple "Via" headers in the SIP message, however we want
+ // to return the first once found, as this corresponds with the transaction
+ // that is relevant here.
+ return pair[1];
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private static String[] splitStartLineAndVerify(String startLine) {
+ String[] splitLine = startLine.split(" ");
+ if (isStartLineMalformed(splitLine)) return null;
+ return splitLine;
+ }
+
+ private static boolean isStartLineMalformed(String[] startLine) {
+ if (startLine == null || startLine.length == 0) {
+ return true;
+ }
+ // start lines contain three segments separated by spaces (SP):
+ // Request-Line = Method SP Request-URI SP SIP-Version CRLF
+ // Status-Line = SIP-Version SP Status-Code SP Reason-Phrase CRLF
+ return (startLine.length != 3);
+ }
+
+ private static boolean verifySipRequest(String[] request) {
+ // Request-Line = Method SP Request-URI SP SIP-Version CRLF
+ boolean verified = request[2].contains(SIP_VERSION_2);
+ verified &= (Uri.parse(request[1]).getScheme() != null);
+ verified &= Arrays.stream(SIP_REQUEST_METHODS).anyMatch(s -> request[0].contains(s));
+ return verified;
+ }
+
+ private static boolean verifySipResponse(String[] response) {
+ // Status-Line = SIP-Version SP Status-Code SP Reason-Phrase CRLF
+ boolean verified = response[0].contains(SIP_VERSION_2);
+ int statusCode = Integer.parseInt(response[1]);
+ verified &= (statusCode >= 100 && statusCode < 700);
+ return verified;
+ }
+
+ /**
+ * Parse a String representation of the Header portion of the SIP Message and re-structure it
+ * into a List of key->value pairs representing each header in the order that they appeared in
+ * the message.
+ *
+ * @param headerString The raw string containing all headers
+ * @param stopAtFirstMatch Return early when the first match is found from matching header keys.
+ * @param matchingHeaderKeys An optional list of Strings containing header keys that should be
+ * returned if they exist. If none exist, all keys will be returned.
+ * (This is internally an equalsIgnoreMatch comparison).
+ * @return the matched header keys and values.
+ */
+ private static List<Pair<String, String>> parseHeaders(String headerString,
+ boolean stopAtFirstMatch, String... matchingHeaderKeys) {
+ // Ensure there is no leading whitespace
+ headerString = removeLeadingWhitespace(headerString);
+
+ List<Pair<String, String>> result = new ArrayList<>();
+ // Split the string line-by-line.
+ String[] headerLines = headerString.split("\\r?\\n");
+ if (headerLines.length == 0) {
+ return Collections.emptyList();
+ }
+
+ String headerKey = null;
+ StringBuilder headerValueSegment = new StringBuilder();
+ // loop through each line, either parsing a "key: value" pair or appending values that span
+ // multiple lines.
+ for (String line : headerLines) {
+ // This line is a continuation of the last line if it starts with whitespace or tab
+ if (line.startsWith("\t") || line.startsWith(" ")) {
+ headerValueSegment.append(removeLeadingWhitespace(line));
+ continue;
+ }
+ // This line is the start of a new key, If headerKey/value is already populated from a
+ // previous key/value pair, add it to list of parsed header pairs.
+ if (headerKey != null) {
+ final String key = headerKey;
+ if (matchingHeaderKeys == null || matchingHeaderKeys.length == 0
+ || Arrays.stream(matchingHeaderKeys).anyMatch(
+ (s) -> s.equalsIgnoreCase(key))) {
+ result.add(new Pair<>(key, headerValueSegment.toString()));
+ if (stopAtFirstMatch) {
+ return result;
+ }
+ }
+ headerKey = null;
+ headerValueSegment = new StringBuilder();
+ }
+
+ // Format is "Key:Value", ignore any ":" after the first.
+ String[] pair = line.split(HEADER_KEY_VALUE_SEPARATOR, 2);
+ if (pair.length < 2) {
+ // malformed line, skip
+ Log.w(TAG, "parseHeaders - received malformed line: " + line);
+ continue;
+ }
+
+ headerKey = pair[0].trim();
+ for (int i = 1; i < pair.length; i++) {
+ headerValueSegment.append(removeLeadingWhitespace(pair[i]));
+ }
+ }
+ // Pick up the last pending header being parsed, if it exists.
+ if (headerKey != null) {
+ final String key = headerKey;
+ if (matchingHeaderKeys == null || matchingHeaderKeys.length == 0
+ || Arrays.stream(matchingHeaderKeys).anyMatch(
+ (s) -> s.equalsIgnoreCase(key))) {
+ result.add(new Pair<>(key, headerValueSegment.toString()));
+ }
+ }
+
+ return result;
+ }
+
+ private static String removeLeadingWhitespace(String line) {
+ return line.replaceFirst("^\\s*", "");
+ }
+}
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index 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 7e019cc..daa3d1f0 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3591,6 +3591,28 @@
*/
public static final String ENABLE_EAP_METHOD_PREFIX_BOOL = "enable_eap_method_prefix_bool";
+
+ /**
+ * Configs used by ImsServiceEntitlement.
+ */
+ public static final class ImsServiceEntitlement {
+ private ImsServiceEntitlement() {}
+
+ /** Prefix of all ImsServiceEntitlement.KEY_* constants. */
+ public static final String KEY_PREFIX = "imsserviceentitlement.";
+
+
+ /** The address of the entitlement configuration server. */
+ public static final String KEY_AES_URL_STRING = KEY_PREFIX + "aes_url_string";
+
+
+ private static PersistableBundle getDefaults() {
+ PersistableBundle defaults = new PersistableBundle();
+ defaults.putString(KEY_AES_URL_STRING, "");
+ return defaults;
+ }
+ }
+
/**
* GPS configs. See the GNSS HAL documentation for more details.
*/
@@ -4621,6 +4643,13 @@
*/
public static final String KEY_USE_ACS_FOR_RCS_BOOL = "use_acs_for_rcs_bool";
+ /**
+ * Indicates temporarily unmetered mobile data is supported by the carrier.
+ * @hide
+ */
+ public static final String KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL =
+ "network_temp_not_metered_supported_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -5119,6 +5148,7 @@
sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG, 10000);
/* Default value is 60 seconds. */
sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG, 60000);
+ sDefaults.putAll(ImsServiceEntitlement.getDefaults());
sDefaults.putAll(Gps.getDefaults());
sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
new int[] {
@@ -5170,6 +5200,7 @@
sDefaults.putString(KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING, "");
sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false);
sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false);
+ sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, false);
}
/**
@@ -5188,9 +5219,25 @@
public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT =
KEY_PREFIX + "hotspot_maximum_client_count";
+ /**
+ * This configuration is intended to be a narrow exception for provisioning
+ * {@link android.net.wifi.WifiNetworkSuggestion} of widely-known carrier networks that do
+ * not support using randomized MAC address.
+ * Carrier provisioned {@link android.net.wifi.WifiNetworkSuggestion} with SSIDs included
+ * in this list will have MAC randomization disabled.
+ *
+ * Note: the SSIDs in the list are expected to be interpreted as is - do not add double
+ * quotes to the SSIDs.
+ */
+ public static final String KEY_SUGGESTION_SSID_LIST_WITH_MAC_RANDOMIZATION_DISABLED =
+ KEY_PREFIX + "suggestion_ssid_list_with_mac_randomization_disabled";
+
private static PersistableBundle getDefaults() {
PersistableBundle defaults = new PersistableBundle();
defaults.putInt(KEY_HOTSPOT_MAX_CLIENT_COUNT, 0);
+ defaults.putStringArray(KEY_SUGGESTION_SSID_LIST_WITH_MAC_RANDOMIZATION_DISABLED,
+ new String[0]);
+
return defaults;
}
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index ed09d53..1273aa3a 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -478,7 +478,9 @@
/**
* Compare phone numbers a and b, return true if they're identical enough for caller ID purposes.
+ * @deprecated use {@link #areSamePhoneNumber(String, String, String)} instead
*/
+ @Deprecated
public static boolean compare(String a, String b) {
// We've used loose comparation at least Eclair, which may change in the future.
@@ -489,7 +491,9 @@
* Compare phone numbers a and b, and return true if they're identical
* enough for caller ID purposes. Checks a resource to determine whether
* to use a strict or loose comparison algorithm.
+ * @deprecated use {@link #areSamePhoneNumber(String, String, String)} instead
*/
+ @Deprecated
public static boolean compare(Context context, String a, String b) {
boolean useStrict = context.getResources().getBoolean(
com.android.internal.R.bool.config_use_strict_phone_number_comparation);
@@ -3218,7 +3222,7 @@
}
// The conversion map is not defined (this is default). Skip conversion.
- if (sConvertToEmergencyMap == null || sConvertToEmergencyMap.length == 0 ) {
+ if (sConvertToEmergencyMap == null || sConvertToEmergencyMap.length == 0) {
return number;
}
@@ -3254,4 +3258,47 @@
}
return number;
}
+
+ /**
+ * Determines if two phone numbers are the same.
+ * <p>
+ * Matching is based on <a href="https://github.com/google/libphonenumber>libphonenumber</a>.
+ * Unlike {@link #compare(String, String)}, matching takes into account national
+ * dialing plans rather than simply matching the last 7 digits of the two phone numbers. As a
+ * result, it is expected that some numbers which would match using the previous method will no
+ * longer match using this new approach.
+ *
+ * @param number1
+ * @param number2
+ * @param defaultCountryIso The lowercase two letter ISO 3166-1 country code. Used when parsing
+ * the phone numbers where it is not possible to determine the country
+ * associated with a phone number based on the number alone. It
+ * is recommended to pass in
+ * {@link TelephonyManager#getNetworkCountryIso()}.
+ * @return True if the two given phone number are same.
+ */
+ public static boolean areSamePhoneNumber(@NonNull String number1,
+ @NonNull String number2, @NonNull String defaultCountryIso) {
+ PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+ PhoneNumber n1;
+ PhoneNumber n2;
+ defaultCountryIso = defaultCountryIso.toUpperCase();
+ try {
+ n1 = util.parseAndKeepRawInput(number1, defaultCountryIso);
+ n2 = util.parseAndKeepRawInput(number2, defaultCountryIso);
+ } catch (NumberParseException e) {
+ return false;
+ }
+
+ PhoneNumberUtil.MatchType matchType = util.isNumberMatch(n1, n2);
+ if (matchType == PhoneNumberUtil.MatchType.EXACT_MATCH
+ || matchType == PhoneNumberUtil.MatchType.NSN_MATCH) {
+ return true;
+ } else if (matchType == PhoneNumberUtil.MatchType.SHORT_NSN_MATCH) {
+ return (n1.getNationalNumber() == n2.getNationalNumber()
+ && n1.getCountryCode() == n2.getCountryCode());
+ } else {
+ return false;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index 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 f6f6d75..0059ad6 100644
--- a/telephony/java/android/telephony/SignalThresholdInfo.java
+++ b/telephony/java/android/telephony/SignalThresholdInfo.java
@@ -28,101 +28,109 @@
/**
* Defines the threshold value of the signal strength.
- * @hide
*/
-public class SignalThresholdInfo implements Parcelable {
+public final class SignalThresholdInfo implements Parcelable {
+
+ /**
+ * Unknown signal measurement type.
+ */
+ public static final int SIGNAL_MEASUREMENT_TYPE_UNKNOWN = 0;
+
/**
* Received Signal Strength Indication.
* Range: -113 dBm and -51 dBm
- * Used RAN: GERAN, CDMA2000
+ * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#GERAN},
+ * {@link AccessNetworkConstants.AccessNetworkType#CDMA2000}
* Reference: 3GPP TS 27.007 section 8.5.
*/
- public static final int SIGNAL_RSSI = 1;
+ public static final int SIGNAL_MEASUREMENT_TYPE_RSSI = 1;
/**
* Received Signal Code Power.
* Range: -120 dBm to -25 dBm;
- * Used RAN: UTRAN
+ * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#UTRAN}
* Reference: 3GPP TS 25.123, section 9.1.1.1
*/
- public static final int SIGNAL_RSCP = 2;
+ public static final int SIGNAL_MEASUREMENT_TYPE_RSCP = 2;
/**
* Reference Signal Received Power.
* Range: -140 dBm to -44 dBm;
- * Used RAN: EUTRAN
+ * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
* Reference: 3GPP TS 36.133 9.1.4
*/
- public static final int SIGNAL_RSRP = 3;
+ public static final int SIGNAL_MEASUREMENT_TYPE_RSRP = 3;
/**
* Reference Signal Received Quality
- * Range: -20 dB to -3 dB;
- * Used RAN: EUTRAN
+ * Range: -34 dB to 3 dB;
+ * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
* Reference: 3GPP TS 36.133 9.1.7
*/
- public static final int SIGNAL_RSRQ = 4;
+ public static final int SIGNAL_MEASUREMENT_TYPE_RSRQ = 4;
/**
* Reference Signal Signal to Noise Ratio
* Range: -20 dB to 30 dB;
- * Used RAN: EUTRAN
+ * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
*/
- public static final int SIGNAL_RSSNR = 5;
+ public static final int SIGNAL_MEASUREMENT_TYPE_RSSNR = 5;
/**
* 5G SS reference signal received power.
* Range: -140 dBm to -44 dBm.
- * Used RAN: NGRAN
+ * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
* Reference: 3GPP TS 38.215.
*/
- public static final int SIGNAL_SSRSRP = 6;
+ public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRP = 6;
/**
* 5G SS reference signal received quality.
- * Range: -20 dB to -3 dB.
- * Used RAN: NGRAN
- * Reference: 3GPP TS 38.215.
+ * Range: -43 dB to 20 dB.
+ * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
+ * Reference: 3GPP TS 38.133 section 10.1.11.1.
*/
- public static final int SIGNAL_SSRSRQ = 7;
+ public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRQ = 7;
/**
* 5G SS signal-to-noise and interference ratio.
* Range: -23 dB to 40 dB
- * Used RAN: NGRAN
+ * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
* Reference: 3GPP TS 38.215 section 5.1.*, 3GPP TS 38.133 section 10.1.16.1.
*/
- public static final int SIGNAL_SSSINR = 8;
+ public static final int SIGNAL_MEASUREMENT_TYPE_SSSINR = 8;
/** @hide */
- @IntDef(prefix = { "SIGNAL_" }, value = {
- SIGNAL_RSSI,
- SIGNAL_RSCP,
- SIGNAL_RSRP,
- SIGNAL_RSRQ,
- SIGNAL_RSSNR,
- SIGNAL_SSRSRP,
- SIGNAL_SSRSRQ,
- SIGNAL_SSSINR
+ @IntDef(prefix = {"SIGNAL_MEASUREMENT_TYPE_"}, value = {
+ SIGNAL_MEASUREMENT_TYPE_UNKNOWN,
+ SIGNAL_MEASUREMENT_TYPE_RSSI,
+ SIGNAL_MEASUREMENT_TYPE_RSCP,
+ SIGNAL_MEASUREMENT_TYPE_RSRP,
+ SIGNAL_MEASUREMENT_TYPE_RSRQ,
+ SIGNAL_MEASUREMENT_TYPE_RSSNR,
+ SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+ SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
+ SIGNAL_MEASUREMENT_TYPE_SSSINR
})
@Retention(RetentionPolicy.SOURCE)
- public @interface SignalMeasurementType {}
+ public @interface SignalMeasurementType {
+ }
@SignalMeasurementType
- private int mSignalMeasurement;
+ private final int mSignalMeasurementType;
/**
* A hysteresis time in milliseconds to prevent flapping.
* A value of 0 disables hysteresis
*/
- private int mHysteresisMs;
+ private final int mHysteresisMs;
/**
* An interval in dB defining the required magnitude change between reports.
* hysteresisDb must be smaller than the smallest threshold delta.
* An interval value of 0 disables hysteresis.
*/
- private int mHysteresisDb;
+ private final int mHysteresisDb;
/**
* List of threshold values.
@@ -130,60 +138,399 @@
* The threshold values for which to apply criteria.
* A vector size of 0 disables the use of thresholds for reporting.
*/
- private int[] mThresholds = null;
+ private final int[] mThresholds;
/**
* {@code true} means modem must trigger the report based on the criteria;
* {@code false} means modem must not trigger the report based on the criteria.
*/
- private boolean mIsEnabled = true;
+ private final boolean mIsEnabled;
+
+ /**
+ * The radio access network type associated with the signal thresholds.
+ */
+ @AccessNetworkConstants.RadioAccessNetworkType
+ private final int mRan;
/**
* Indicates the hysteresisMs is disabled.
+ *
+ * @hide
*/
public static final int HYSTERESIS_MS_DISABLED = 0;
/**
* Indicates the hysteresisDb is disabled.
+ *
+ * @hide
*/
public static final int HYSTERESIS_DB_DISABLED = 0;
+
+ /**
+ * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSI}.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_RSSI_MIN_VALUE = -113;
+
+ /**
+ * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSI}.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_RSSI_MAX_VALUE = -51;
+
+ /**
+ * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSCP}.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_RSCP_MIN_VALUE = -120;
+
+ /**
+ * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSCP}.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_RSCP_MAX_VALUE = -25;
+
+ /**
+ * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRP}.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_RSRP_MIN_VALUE = -140;
+
+ /**
+ * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRP}.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_RSRP_MAX_VALUE = -44;
+
+ /**
+ * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRQ}.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_RSRQ_MIN_VALUE = -34;
+
+ /**
+ * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRQ}.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_RSRQ_MAX_VALUE = 3;
+
+ /**
+ * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSNR}.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_RSSNR_MIN_VALUE = -20;
+
+ /**
+ * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSNR}.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_RSSNR_MAX_VALUE = 30;
+
+ /**
+ * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRP}.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_SSRSRP_MIN_VALUE = -140;
+
+ /**
+ * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRP}.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_SSRSRP_MAX_VALUE = -44;
+
+ /**
+ * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRQ}.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_SSRSRQ_MIN_VALUE = -43;
+
+ /**
+ * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRQ}.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_SSRSRQ_MAX_VALUE = 20;
+
+ /**
+ * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSSINR}.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_SSSINR_MIN_VALUE = -23;
+
+ /**
+ * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSSINR}.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_SSSINR_MAX_VALUE = 40;
+
+ /**
+ * The minimum number of thresholds allowed in each SignalThresholdInfo.
+ *
+ * @hide
+ */
+ public static final int MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED = 1;
+
+ /**
+ * The maximum number of thresholds allowed in each SignalThresholdInfo.
+ *
+ * @hide
+ */
+ public static final int MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED = 4;
+
/**
* Constructor
*
- * @param signalMeasurement Signal Measurement Type
- * @param hysteresisMs hysteresisMs
- * @param hysteresisDb hysteresisDb
- * @param thresholds threshold value
- * @param isEnabled isEnabled
+ * @param ran Radio Access Network type
+ * @param signalMeasurementType Signal Measurement Type
+ * @param hysteresisMs hysteresisMs
+ * @param hysteresisDb hysteresisDb
+ * @param thresholds threshold value
+ * @param isEnabled isEnabled
*/
- public SignalThresholdInfo(@SignalMeasurementType int signalMeasurement,
- int hysteresisMs, int hysteresisDb, @NonNull int [] thresholds, boolean isEnabled) {
- mSignalMeasurement = signalMeasurement;
+ private SignalThresholdInfo(@AccessNetworkConstants.RadioAccessNetworkType int ran,
+ @SignalMeasurementType int signalMeasurementType, int hysteresisMs, int hysteresisDb,
+ @NonNull int[] thresholds, boolean isEnabled) {
+ Objects.requireNonNull(thresholds, "thresholds must not be null");
+ validateRanWithMeasurementType(ran, signalMeasurementType);
+ validateThresholdRange(signalMeasurementType, thresholds);
+
+ mRan = ran;
+ mSignalMeasurementType = signalMeasurementType;
mHysteresisMs = hysteresisMs < 0 ? HYSTERESIS_MS_DISABLED : hysteresisMs;
mHysteresisDb = hysteresisDb < 0 ? HYSTERESIS_DB_DISABLED : hysteresisDb;
- mThresholds = thresholds == null ? null : thresholds.clone();
+ mThresholds = thresholds;
mIsEnabled = isEnabled;
}
- public @SignalMeasurementType int getSignalMeasurement() {
- return mSignalMeasurement;
+ /**
+ * Builder class to create {@link SignalThresholdInfo} objects.
+ */
+ public static final class Builder {
+ private int mRan = AccessNetworkConstants.AccessNetworkType.UNKNOWN;
+ private int mSignalMeasurementType = SIGNAL_MEASUREMENT_TYPE_UNKNOWN;
+ private int mHysteresisMs = HYSTERESIS_MS_DISABLED;
+ private int mHysteresisDb = HYSTERESIS_DB_DISABLED;
+ private int[] mThresholds = null;
+ private boolean mIsEnabled = false;
+
+ /**
+ * Set the radio access network type for the builder instance.
+ *
+ * @param ran The radio access network type
+ * @return the builder to facilitate the chaining
+ */
+ public @NonNull Builder setRadioAccessNetworkType(
+ @AccessNetworkConstants.RadioAccessNetworkType int ran) {
+ mRan = ran;
+ return this;
+ }
+
+ /**
+ * Set the signal measurement type for the builder instance.
+ *
+ * @param signalMeasurementType The signal measurement type
+ * @return the builder to facilitate the chaining
+ */
+ public @NonNull Builder setSignalMeasurementType(
+ @SignalMeasurementType int signalMeasurementType) {
+ mSignalMeasurementType = signalMeasurementType;
+ return this;
+ }
+
+ /**
+ * Set the hysteresis time in milliseconds to prevent flapping. A value of 0 disables
+ * hysteresis.
+ *
+ * @param hysteresisMs the hysteresis time in milliseconds
+ * @return the builder to facilitate the chaining
+ * @hide
+ */
+ public @NonNull Builder setHysteresisMs(int hysteresisMs) {
+ mHysteresisMs = hysteresisMs;
+ return this;
+ }
+
+ /**
+ * Set the interval in dB defining the required magnitude change between reports. A value of
+ * zero disabled dB-based hysteresis restrictions.
+ *
+ * @param hysteresisDb the interval in dB
+ * @return the builder to facilitate the chaining
+ * @hide
+ */
+ public @NonNull Builder setHysteresisDb(int hysteresisDb) {
+ mHysteresisDb = hysteresisDb;
+ return this;
+ }
+
+ /**
+ * Set the signal strength thresholds of the corresponding signal measurement type.
+ *
+ * The range and unit must reference specific SignalMeasurementType. The length of the
+ * thresholds should between the numbers return from
+ * {@link #getMinimumNumberOfThresholdsAllowed()} and
+ * {@link #getMaximumNumberOfThresholdsAllowed()}. An IllegalArgumentException will throw
+ * otherwise.
+ *
+ * @param thresholds array of integer as the signal threshold values
+ * @return the builder to facilitate the chaining
+ *
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSSI
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSCP
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSRP
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSRQ
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSSNR
+ * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRP
+ * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRQ
+ * @see #SIGNAL_MEASUREMENT_TYPE_SSSINR
+ * @see #getThresholds() for more details on signal strength thresholds
+ */
+ public @NonNull Builder setThresholds(@NonNull int[] thresholds) {
+ Objects.requireNonNull(thresholds, "thresholds must not be null");
+ if (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
+ || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED) {
+ throw new IllegalArgumentException(
+ "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
+ + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED);
+ }
+ mThresholds = thresholds.clone();
+ Arrays.sort(mThresholds);
+ return this;
+ }
+
+ /**
+ * Set the signal strength thresholds for the corresponding signal measurement type without
+ * the length limitation.
+ *
+ * @param thresholds array of integer as the signal threshold values
+ * @return the builder to facilitate the chaining
+ *
+ * @hide
+ */
+ public @NonNull Builder setThresholdsUnlimited(@NonNull int[] thresholds) {
+ Objects.requireNonNull(thresholds, "thresholds must not be null");
+ mThresholds = thresholds.clone();
+ Arrays.sort(mThresholds);
+ return this;
+ }
+
+
+ /**
+ * Set if the modem should trigger the report based on the criteria.
+ *
+ * @param isEnabled true if the modem should trigger the report based on the criteria
+ * @return the builder to facilitate the chaining
+ * @hide
+ */
+ public @NonNull Builder setIsEnabled(boolean isEnabled) {
+ mIsEnabled = isEnabled;
+ return this;
+ }
+
+ /**
+ * Build {@link SignalThresholdInfo} object.
+ *
+ * @return the SignalThresholdInfo object build out
+ *
+ * @throws IllegalArgumentException if the signal measurement type is invalid, any value in
+ * the thresholds is out of range, or the RAN is not allowed to set with the signal
+ * measurement type
+ */
+ public @NonNull SignalThresholdInfo build() {
+ return new SignalThresholdInfo(mRan, mSignalMeasurementType, mHysteresisMs,
+ mHysteresisDb, mThresholds, mIsEnabled);
+ }
}
+ /**
+ * Get the radio access network type.
+ *
+ * @return radio access network type
+ */
+ public @AccessNetworkConstants.RadioAccessNetworkType int getRadioAccessNetworkType() {
+ return mRan;
+ }
+
+ /**
+ * Get the signal measurement type.
+ *
+ * @return the SignalMeasurementType value
+ */
+ public @SignalMeasurementType int getSignalMeasurementType() {
+ return mSignalMeasurementType;
+ }
+
+ /** @hide */
public int getHysteresisMs() {
return mHysteresisMs;
}
+ /** @hide */
public int getHysteresisDb() {
return mHysteresisDb;
}
+ /** @hide */
public boolean isEnabled() {
return mIsEnabled;
}
- public int[] getThresholds() {
- return mThresholds == null ? null : mThresholds.clone();
+ /**
+ * Get the signal strength thresholds.
+ *
+ * Signal strength thresholds are a list of integer used for suggesting signal level and signal
+ * reporting criteria. The range and unit must reference specific SignalMeasurementType.
+ *
+ * Please refer to https://source.android.com/devices/tech/connect/signal-strength on how signal
+ * strength thresholds are used for signal strength reporting.
+ *
+ * @return array of integer of the signal thresholds
+ *
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSSI
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSCP
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSRP
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSRQ
+ * @see #SIGNAL_MEASUREMENT_TYPE_RSSNR
+ * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRP
+ * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRQ
+ * @see #SIGNAL_MEASUREMENT_TYPE_SSSINR
+ */
+ public @NonNull int[] getThresholds() {
+ return mThresholds.clone();
+ }
+
+ /**
+ * Get the minimum number of thresholds allowed in each SignalThresholdInfo.
+ *
+ * @return the minimum number of thresholds allowed
+ */
+ public static int getMinimumNumberOfThresholdsAllowed() {
+ return MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED;
+ }
+
+ /**
+ * Get the maximum number of threshold allowed in each SignalThresholdInfo.
+ *
+ * @return the maximum number of thresholds allowed
+ */
+ public static int getMaximumNumberOfThresholdsAllowed() {
+ return MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED;
}
@Override
@@ -192,8 +539,9 @@
}
@Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(mSignalMeasurement);
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(mRan);
+ out.writeInt(mSignalMeasurementType);
out.writeInt(mHysteresisMs);
out.writeInt(mHysteresisDb);
out.writeIntArray(mThresholds);
@@ -201,7 +549,8 @@
}
private SignalThresholdInfo(Parcel in) {
- mSignalMeasurement = in.readInt();
+ mRan = in.readInt();
+ mSignalMeasurementType = in.readInt();
mHysteresisMs = in.readInt();
mHysteresisDb = in.readInt();
mThresholds = in.createIntArray();
@@ -217,7 +566,8 @@
}
SignalThresholdInfo other = (SignalThresholdInfo) o;
- return mSignalMeasurement == other.mSignalMeasurement
+ return mRan == other.mRan
+ && mSignalMeasurementType == other.mSignalMeasurementType
&& mHysteresisMs == other.mHysteresisMs
&& mHysteresisDb == other.mHysteresisDb
&& Arrays.equals(mThresholds, other.mThresholds)
@@ -226,8 +576,8 @@
@Override
public int hashCode() {
- return Objects.hash(
- mSignalMeasurement, mHysteresisMs, mHysteresisDb, mThresholds, mIsEnabled);
+ return Objects.hash(mRan, mSignalMeasurementType, mHysteresisMs, mHysteresisDb, mThresholds,
+ mIsEnabled);
}
public static final @NonNull Parcelable.Creator<SignalThresholdInfo> CREATOR =
@@ -246,11 +596,83 @@
@Override
public String toString() {
return new StringBuilder("SignalThresholdInfo{")
- .append("mSignalMeasurement=").append(mSignalMeasurement)
- .append("mHysteresisMs=").append(mSignalMeasurement)
- .append("mHysteresisDb=").append(mHysteresisDb)
- .append("mThresholds=").append(Arrays.toString(mThresholds))
- .append("mIsEnabled=").append(mIsEnabled)
- .append("}").toString();
+ .append("mRan=").append(mRan)
+ .append(" mSignalMeasurementType=").append(mSignalMeasurementType)
+ .append(" mHysteresisMs=").append(mHysteresisMs)
+ .append(" mHysteresisDb=").append(mHysteresisDb)
+ .append(" mThresholds=").append(Arrays.toString(mThresholds))
+ .append(" mIsEnabled=").append(mIsEnabled)
+ .append("}").toString();
+ }
+
+ /**
+ * Return true if signal measurement type is valid and the threshold value is in range.
+ */
+ private static boolean isValidThreshold(@SignalMeasurementType int type, int threshold) {
+ switch (type) {
+ case SIGNAL_MEASUREMENT_TYPE_RSSI:
+ return threshold >= SIGNAL_RSSI_MIN_VALUE && threshold <= SIGNAL_RSSI_MAX_VALUE;
+ case SIGNAL_MEASUREMENT_TYPE_RSCP:
+ return threshold >= SIGNAL_RSCP_MIN_VALUE && threshold <= SIGNAL_RSCP_MAX_VALUE;
+ case SIGNAL_MEASUREMENT_TYPE_RSRP:
+ return threshold >= SIGNAL_RSRP_MIN_VALUE && threshold <= SIGNAL_RSRP_MAX_VALUE;
+ case SIGNAL_MEASUREMENT_TYPE_RSRQ:
+ return threshold >= SIGNAL_RSRQ_MIN_VALUE && threshold <= SIGNAL_RSRQ_MAX_VALUE;
+ case SIGNAL_MEASUREMENT_TYPE_RSSNR:
+ return threshold >= SIGNAL_RSSNR_MIN_VALUE && threshold <= SIGNAL_RSSNR_MAX_VALUE;
+ case SIGNAL_MEASUREMENT_TYPE_SSRSRP:
+ return threshold >= SIGNAL_SSRSRP_MIN_VALUE && threshold <= SIGNAL_SSRSRP_MAX_VALUE;
+ case SIGNAL_MEASUREMENT_TYPE_SSRSRQ:
+ return threshold >= SIGNAL_SSRSRQ_MIN_VALUE && threshold <= SIGNAL_SSRSRQ_MAX_VALUE;
+ case SIGNAL_MEASUREMENT_TYPE_SSSINR:
+ return threshold >= SIGNAL_SSSINR_MIN_VALUE && threshold <= SIGNAL_SSSINR_MAX_VALUE;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Return true if the radio access type is allowed to set with the measurement type.
+ */
+ private static boolean isValidRanWithMeasurementType(
+ @AccessNetworkConstants.RadioAccessNetworkType int ran,
+ @SignalMeasurementType int type) {
+ switch (type) {
+ case SIGNAL_MEASUREMENT_TYPE_RSSI:
+ return ran == AccessNetworkConstants.AccessNetworkType.GERAN
+ || ran == AccessNetworkConstants.AccessNetworkType.CDMA2000;
+ case SIGNAL_MEASUREMENT_TYPE_RSCP:
+ return ran == AccessNetworkConstants.AccessNetworkType.UTRAN;
+ case SIGNAL_MEASUREMENT_TYPE_RSRP:
+ case SIGNAL_MEASUREMENT_TYPE_RSRQ:
+ case SIGNAL_MEASUREMENT_TYPE_RSSNR:
+ return ran == AccessNetworkConstants.AccessNetworkType.EUTRAN;
+ case SIGNAL_MEASUREMENT_TYPE_SSRSRP:
+ case SIGNAL_MEASUREMENT_TYPE_SSRSRQ:
+ case SIGNAL_MEASUREMENT_TYPE_SSSINR:
+ return ran == AccessNetworkConstants.AccessNetworkType.NGRAN;
+ default:
+ return false;
+ }
+ }
+
+ private void validateRanWithMeasurementType(
+ @AccessNetworkConstants.RadioAccessNetworkType int ran,
+ @SignalMeasurementType int signalMeasurement) {
+ if (!isValidRanWithMeasurementType(ran, signalMeasurement)) {
+ throw new IllegalArgumentException(
+ "invalid RAN: " + ran + " with signal measurement type: " + signalMeasurement);
+ }
+ }
+
+ private void validateThresholdRange(@SignalMeasurementType int signalMeasurement,
+ int[] thresholds) {
+ for (int threshold : thresholds) {
+ if (!isValidThreshold(signalMeasurement, threshold)) {
+ throw new IllegalArgumentException(
+ "invalid signal measurement type: " + signalMeasurement
+ + " with threshold: " + threshold);
+ }
+ }
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index bfd37cd..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;
@@ -564,9 +566,10 @@
/**
* Indicates the maximum size of the call composure picture.
*
- * Pictures sent via {@link #uploadCallComposerPicture(InputStream, Executor, OutcomeReceiver)}
- * or {@link #uploadCallComposerPicture(Path, Executor, OutcomeReceiver)} must not exceed this
- * size, or an error will be returned via the callback in those methods.
+ * Pictures sent via
+ * {@link #uploadCallComposerPicture(InputStream, String, Executor, OutcomeReceiver)}
+ * or {@link #uploadCallComposerPicture(Path, String, Executor, OutcomeReceiver)} must not
+ * exceed this size, or an error will be returned via the callback in those methods.
*
* @return Maximum file size in bytes.
*/
@@ -4310,6 +4313,15 @@
*/
public static final int ERROR_IO_EXCEPTION = 5;
+ /**
+ * Indicates that the device is currently not connected to a network that's capable of
+ * reaching a carrier's RCS servers.
+ *
+ * Clients should prompt the user to remedy the issue by moving to an area with better
+ * signal, by connecting to a different network, or to retry at another time.
+ */
+ public static final int ERROR_NETWORK_UNAVAILABLE = 6;
+
/** @hide */
@IntDef(prefix = {"ERROR_"}, value = {
ERROR_UNKNOWN,
@@ -4318,7 +4330,9 @@
ERROR_AUTHENTICATION_FAILED,
ERROR_INPUT_CLOSED,
ERROR_IO_EXCEPTION,
+ ERROR_NETWORK_UNAVAILABLE,
})
+
@Retention(RetentionPolicy.SOURCE)
public @interface CallComposerError {}
@@ -4355,14 +4369,16 @@
/**
* Uploads a picture to the carrier network for use with call composer.
*
- * @see #uploadCallComposerPicture(InputStream, Executor, OutcomeReceiver)
+ * @see #uploadCallComposerPicture(InputStream, String, Executor, OutcomeReceiver)
* @param pictureToUpload Path to a local file containing the picture to upload.
+ * @param contentType The MIME type of the picture you're uploading (e.g. image/jpeg)
* @param executor The {@link Executor} on which the {@code pictureToUpload} file will be read
* from disk, as well as on which {@code callback} will be called.
* @param callback A callback called when the upload operation terminates, either in success
* or in error.
*/
public void uploadCallComposerPicture(@NonNull Path pictureToUpload,
+ @NonNull String contentType,
@CallbackExecutor @NonNull Executor executor,
@NonNull OutcomeReceiver<ParcelUuid, CallComposerException> callback) {
Objects.requireNonNull(pictureToUpload);
@@ -4390,7 +4406,7 @@
}
InputStream fileStream = Files.newInputStream(pictureToUpload);
try {
- uploadCallComposerPicture(fileStream, executor,
+ uploadCallComposerPicture(fileStream, contentType, executor,
new OutcomeReceiver<ParcelUuid, CallComposerException>() {
@Override
public void onResult(ParcelUuid result) {
@@ -4455,12 +4471,14 @@
* of {@link #getMaximumCallComposerPictureSize()}, the upload will be
* aborted and the callback will be called with an exception containing
* {@link CallComposerException#ERROR_FILE_TOO_LARGE}.
+ * @param contentType The MIME type of the picture you're uploading (e.g. image/jpeg)
* @param executor The {@link Executor} on which the {@code pictureToUpload} stream will be
* read, as well as on which the callback will be called.
* @param callback A callback called when the upload operation terminates, either in success
* or in error.
*/
public void uploadCallComposerPicture(@NonNull InputStream pictureToUpload,
+ @NonNull String contentType,
@CallbackExecutor @NonNull Executor executor,
@NonNull OutcomeReceiver<ParcelUuid, CallComposerException> callback) {
Objects.requireNonNull(pictureToUpload);
@@ -4488,7 +4506,7 @@
try {
telephony.uploadCallComposerPicture(getSubId(), mContext.getOpPackageName(),
- readFd, new ResultReceiver(null) {
+ contentType, readFd, new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle result) {
if (resultCode != CallComposerException.SUCCESS) {
@@ -9029,11 +9047,18 @@
*/
public static final int CALL_COMPOSER_STATUS_ON = 1;
+ /**
+ * Call composer status indicating that sending/receiving pictures is disabled.
+ * All other attachments are still enabled in this state.
+ */
+ public static final int CALL_COMPOSER_STATUS_ON_NO_PICTURES = 2;
+
/** @hide */
@IntDef(prefix = {"CALL_COMPOSER_STATUS_"},
value = {
CALL_COMPOSER_STATUS_ON,
CALL_COMPOSER_STATUS_OFF,
+ CALL_COMPOSER_STATUS_ON_NO_PICTURES,
})
public @interface CallComposerStatus {}
@@ -9041,8 +9066,9 @@
* Set the user-set status for enriched calling with call composer.
*
* @param status user-set status for enriched calling with call composer;
- * it must be a value of either {@link #CALL_COMPOSER_STATUS_ON}
- * or {@link #CALL_COMPOSER_STATUS_OFF}.
+ * it must be any of {@link #CALL_COMPOSER_STATUS_ON}
+ * {@link #CALL_COMPOSER_STATUS_OFF},
+ * or {@link #CALL_COMPOSER_STATUS_ON_NO_PICTURES}
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
* given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
@@ -9052,7 +9078,8 @@
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setCallComposerStatus(@CallComposerStatus int status) {
- if (status != CALL_COMPOSER_STATUS_ON && status != CALL_COMPOSER_STATUS_OFF) {
+ if (status > CALL_COMPOSER_STATUS_ON_NO_PICTURES
+ || status < CALL_COMPOSER_STATUS_OFF) {
throw new IllegalArgumentException("requested status is invalid");
}
try {
@@ -9074,8 +9101,9 @@
*
* @throws SecurityException if the caller does not have the permission.
*
- * @return the user-set status for enriched calling with call composer either
- * {@link #CALL_COMPOSER_STATUS_ON} or {@link #CALL_COMPOSER_STATUS_OFF}.
+ * @return the user-set status for enriched calling with call composer, any of
+ * {@link #CALL_COMPOSER_STATUS_ON}, {@link #CALL_COMPOSER_STATUS_OFF}, or
+ * {@link #CALL_COMPOSER_STATUS_ON_NO_PICTURES}.
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @CallComposerStatus int getCallComposerStatus() {
@@ -15159,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/telephony/java/android/telephony/ims/DelegateRegistrationState.java b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
index 66281ed..fd206c1 100644
--- a/telephony/java/android/telephony/ims/DelegateRegistrationState.java
+++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
@@ -320,4 +320,11 @@
public int hashCode() {
return Objects.hash(mRegisteredTags, mDeregisteringTags, mDeregisteredTags);
}
+
+ @Override
+ public String toString() {
+ return "DelegateRegistrationState{ registered={" + mRegisteredTags
+ + "}, deregistering={" + mDeregisteringTags + "}, deregistered={"
+ + mDeregisteredTags + "}}";
+ }
}
diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java
index 1539224..006cca8 100644
--- a/telephony/java/android/telephony/ims/SipMessage.java
+++ b/telephony/java/android/telephony/ims/SipMessage.java
@@ -22,6 +22,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.SipMessageParsingUtils;
+
import java.util.Arrays;
import java.util.Objects;
@@ -38,9 +40,6 @@
// Should not be set to true for production!
private static final boolean IS_DEBUGGING = Build.IS_ENG;
- private static final String[] SIP_REQUEST_METHODS = new String[] {"INVITE", "ACK", "OPTIONS",
- "BYE", "CANCEL", "REGISTER"};
-
private final String mStartLine;
private final String mHeaderSection;
private final byte[] mContent;
@@ -72,6 +71,7 @@
mContent = new byte[source.readInt()];
source.readByteArray(mContent);
}
+
/**
* @return The start line of the SIP message, which contains either the request-line or
* status-line.
@@ -128,34 +128,25 @@
} else {
b.append(sanitizeStartLineRequest(mStartLine));
}
- b.append("], [");
- b.append("Header: [");
+ b.append("], Header: [");
if (IS_DEBUGGING) {
b.append(mHeaderSection);
} else {
// only identify transaction id/call ID when it is available.
b.append("***");
}
- b.append("], ");
- b.append("Content: [NOT SHOWN]");
+ b.append("], Content: ");
+ b.append(getContent().length == 0 ? "[NONE]" : "[NOT SHOWN]");
return b.toString();
}
/**
- * Start lines containing requests are formatted: METHOD SP Request-URI SP SIP-Version CRLF.
* Detect if this is a REQUEST and redact Request-URI portion here, as it contains PII.
*/
private String sanitizeStartLineRequest(String startLine) {
+ if (!SipMessageParsingUtils.isSipRequest(startLine)) return startLine;
String[] splitLine = startLine.split(" ");
- if (splitLine == null || splitLine.length == 0) {
- return "(INVALID STARTLINE)";
- }
- for (String method : SIP_REQUEST_METHODS) {
- if (splitLine[0].contains(method)) {
- return splitLine[0] + " <Request-URI> " + splitLine[2];
- }
- }
- return startLine;
+ return splitLine[0] + " <Request-URI> " + splitLine[2];
}
@Override
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
index 522ad81..9d91901 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
@@ -28,6 +28,10 @@
import android.telephony.ims.SipDelegateManager;
import android.telephony.ims.SipMessage;
import android.telephony.ims.stub.SipDelegate;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.telephony.SipMessageParsingUtils;
import java.util.ArrayList;
import java.util.Set;
@@ -40,6 +44,7 @@
* @hide
*/
public class SipDelegateAidlWrapper implements DelegateStateCallback, DelegateMessageCallback {
+ private static final String LOG_TAG = "SipDelegateAW";
private final ISipDelegate.Stub mDelegateBinder = new ISipDelegate.Stub() {
@Override
@@ -183,11 +188,15 @@
}
private void notifyLocalMessageFailedToBeReceived(SipMessage m, int reason) {
- //TODO: parse transaction ID or throw IllegalArgumentException if the SipMessage
- // transaction ID can not be parsed.
+ String transactionId = SipMessageParsingUtils.getTransactionId(m.getHeaderSection());
+ if (TextUtils.isEmpty(transactionId)) {
+ Log.w(LOG_TAG, "failure to parse SipMessage.");
+ throw new IllegalArgumentException("Malformed SipMessage, can not determine "
+ + "transaction ID.");
+ }
SipDelegate d = mDelegate;
if (d != null) {
- mExecutor.execute(() -> d.notifyMessageReceiveError(null, reason));
+ mExecutor.execute(() -> d.notifyMessageReceiveError(transactionId, reason));
}
}
}
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
index a35039b..c877aca 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
@@ -28,9 +28,12 @@
import android.telephony.ims.stub.DelegateConnectionMessageCallback;
import android.telephony.ims.stub.DelegateConnectionStateCallback;
import android.telephony.ims.stub.SipDelegate;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
+import com.android.internal.telephony.SipMessageParsingUtils;
+
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.Executor;
@@ -265,9 +268,13 @@
}
private void notifyLocalMessageFailedToSend(SipMessage m, int reason) {
- //TODO: parse transaction ID or throw IllegalArgumentException if the SipMessage
- // transaction ID can not be parsed.
+ String transactionId = SipMessageParsingUtils.getTransactionId(m.getHeaderSection());
+ if (TextUtils.isEmpty(transactionId)) {
+ Log.w(LOG_TAG, "sendMessage detected a malformed SipMessage and can not get a "
+ + "transaction ID.");
+ throw new IllegalArgumentException("Could not send SipMessage due to malformed header");
+ }
mExecutor.execute(() ->
- mMessageCallback.onMessageSendFailure(null, reason));
+ mMessageCallback.onMessageSendFailure(transactionId, reason));
}
}
diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
index d2cb976..d9734a7 100644
--- a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
+++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
@@ -18,15 +18,12 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.net.Uri;
import android.telephony.ims.ImsException;
import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.RcsUceAdapter;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.RcsFeature;
-import java.util.List;
-
/**
* The interface of the capabilities event listener for ImsService to notify the framework of the
* UCE request and status updated.
@@ -84,25 +81,4 @@
* Telephony stack has crashed.
*/
void onUnpublish() throws ImsException;
-
- /**
- * Inform the framework of a query for this device's UCE capabilities.
- * <p>
- * The framework will respond via the
- * {@link OptionsRequestCallback#onRespondToCapabilityRequest} or
- * {@link OptionsRequestCallback#onRespondToCapabilityRequestWithError}
- * @param contactUri The URI associated with the remote contact that is
- * requesting capabilities.
- * @param remoteCapabilities The remote contact's capability information.
- * @param callback The callback of this request which is sent from the remote user.
- * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not
- * currently connected to the framework. This can happen if the {@link RcsFeature} is not
- * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
- * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare
- * cases when the Telephony stack has crashed.
- * @hide
- */
- void onRemoteCapabilityRequest(@NonNull Uri contactUri,
- @NonNull List<String> remoteCapabilities,
- @NonNull OptionsRequestCallback callback) throws ImsException;
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 83fb38b..46c9dde 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1262,7 +1262,7 @@
int getRadioAccessFamily(in int phoneId, String callingPackage);
void uploadCallComposerPicture(int subscriptionId, String callingPackage,
- in ParcelFileDescriptor fd, in ResultReceiver callback);
+ String contentType, in ParcelFileDescriptor fd, in ResultReceiver callback);
/**
* Enables or disables video calling.
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/OWNERS b/tests/StagedInstallTest/OWNERS
index d825dfd..aac68e9 100644
--- a/tests/StagedInstallTest/OWNERS
+++ b/tests/StagedInstallTest/OWNERS
@@ -1 +1,5 @@
include /services/core/java/com/android/server/pm/OWNERS
+
+dariofreni@google.com
+ioffe@google.com
+olilan@google.com
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index 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 8710d23..b2bcfeb 100644
--- a/tests/net/common/java/android/net/CaptivePortalDataTest.kt
+++ b/tests/net/common/java/android/net/CaptivePortalDataTest.kt
@@ -18,12 +18,15 @@
import android.os.Build
import androidx.test.filters.SmallTest
+import com.android.modules.utils.build.SdkLevel
import com.android.testutils.assertParcelSane
import com.android.testutils.assertParcelingIsLossless
+import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.test.assertEquals
@@ -33,6 +36,9 @@
@RunWith(DevSdkIgnoreRunner::class)
@IgnoreUpTo(Build.VERSION_CODES.Q)
class CaptivePortalDataTest {
+ @Rule @JvmField
+ val ignoreRule = DevSdkIgnoreRule()
+
private val data = CaptivePortalData.Builder()
.setRefreshTime(123L)
.setUserPortalUrl(Uri.parse("https://portal.example.com/test"))
@@ -41,14 +47,19 @@
.setBytesRemaining(456L)
.setExpiryTime(789L)
.setCaptive(true)
- .setVenueFriendlyName("venue friendly name")
+ .apply {
+ if (SdkLevel.isAtLeastS()) {
+ setVenueFriendlyName("venue friendly name")
+ }
+ }
.build()
private fun makeBuilder() = CaptivePortalData.Builder(data)
@Test
fun testParcelUnparcel() {
- assertParcelSane(data, fieldCount = 8)
+ val fieldCount = if (SdkLevel.isAtLeastS()) 8 else 7
+ assertParcelSane(data, fieldCount)
assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build())
assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build())
@@ -67,8 +78,11 @@
assertNotEqualsAfterChange { it.setBytesRemaining(789L) }
assertNotEqualsAfterChange { it.setExpiryTime(12L) }
assertNotEqualsAfterChange { it.setCaptive(false) }
- assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") }
- assertNotEqualsAfterChange { it.setVenueFriendlyName(null) }
+
+ if (SdkLevel.isAtLeastS()) {
+ assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") }
+ assertNotEqualsAfterChange { it.setVenueFriendlyName(null) }
+ }
}
@Test
@@ -111,7 +125,7 @@
assertFalse(makeBuilder().setCaptive(false).build().isCaptive)
}
- @Test
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
fun testVenueFriendlyName() {
assertEquals("venue friendly name", data.venueFriendlyName)
}
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 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 6953100..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;
@@ -290,13 +292,16 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -364,8 +369,6 @@
private HandlerThread mAlarmManagerThread;
private TestNetIdManager mNetIdManager;
- @Mock IpConnectivityMetrics.Logger mMetricsService;
- @Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
@Mock DeviceIdleInternal mDeviceIdleInternal;
@Mock INetworkManagementService mNetworkManagementService;
@Mock INetworkStatsService mStatsService;
@@ -385,6 +388,7 @@
@Mock MockableSystemProperties mSystemProperties;
@Mock EthernetManager mEthernetManager;
@Mock NetworkPolicyManager mNetworkPolicyManager;
+ @Mock KeyStore mKeyStore;
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -411,9 +415,6 @@
private class MockContext extends BroadcastInterceptingContext {
private final MockContentResolver mContentResolver;
- // Contains all registered receivers since this object was created. Useful to clear
- // them when needed, as BroadcastInterceptingContext does not provide this facility.
- private final List<BroadcastReceiver> mRegisteredReceivers = new ArrayList<>();
@Spy private Resources mResources;
private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
@@ -550,19 +551,6 @@
public void setPermission(String permission, Integer granted) {
mMockedPermissions.put(permission, granted);
}
-
- @Override
- public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
- mRegisteredReceivers.add(receiver);
- return super.registerReceiver(receiver, filter);
- }
-
- public void clearRegisteredReceivers() {
- // super.unregisterReceiver is a no-op for receivers that are not registered (because
- // they haven't been registered or because they have already been unregistered).
- // For the same reason, don't bother clearing mRegisteredReceivers.
- for (final BroadcastReceiver rcv : mRegisteredReceivers) unregisterReceiver(rcv);
- }
}
private void waitForIdle() {
@@ -591,10 +579,10 @@
}
// Bring up a network that we can use to send messages to ConnectivityService.
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- waitFor(cv);
+ b.expectBroadcast();
Network n = mWiFiNetworkAgent.getNetwork();
assertNotNull(n);
@@ -611,10 +599,10 @@
@Ignore
public void verifyThatNotWaitingForIdleCausesRaceConditions() throws Exception {
// Bring up a network that we can use to send messages to ConnectivityService.
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- waitFor(cv);
+ b.expectBroadcast();
Network n = mWiFiNetworkAgent.getNetwork();
assertNotNull(n);
@@ -1079,6 +1067,15 @@
private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
private VpnInfo mVpnInfo;
+ // These ConditionVariables allow tests to wait for LegacyVpnRunner to be stopped/started.
+ // TODO: this scheme is ad-hoc and error-prone because it does not fail if, for example, the
+ // test expects two starts in a row, or even if the production code calls start twice in a
+ // row. find a better solution. Simply putting a method to create a LegacyVpnRunner into
+ // Vpn.Dependencies doesn't work because LegacyVpnRunner is not a static class and has
+ // extensive access into the internals of Vpn.
+ private ConditionVariable mStartLegacyVpnCv = new ConditionVariable();
+ private ConditionVariable mStopVpnRunnerCv = new ConditionVariable();
+
public MockVpn(int userId) {
super(startHandlerThreadAndReturnLooper(), mServiceContext,
new Dependencies() {
@@ -1092,7 +1089,7 @@
return mDeviceIdleInternal;
}
},
- mNetworkManagementService, mMockNetd, userId, mock(KeyStore.class));
+ mNetworkManagementService, mMockNetd, userId, mKeyStore);
}
public void setUids(Set<UidRange> uids) {
@@ -1204,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;
@@ -1288,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 {
@@ -1300,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()))
@@ -1375,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));
@@ -1514,29 +1556,79 @@
}
/**
- * Return a ConditionVariable that opens when {@code count} numbers of CONNECTIVITY_ACTION
- * broadcasts are received.
+ * Class to simplify expecting broadcasts using BroadcastInterceptingContext.
+ * Ensures that the receiver is unregistered after the expected broadcast is received. This
+ * cannot be done in the BroadcastReceiver itself because BroadcastInterceptingContext runs
+ * the receivers' receive method while iterating over the list of receivers, and unregistering
+ * the receiver during iteration throws ConcurrentModificationException.
*/
- private ConditionVariable registerConnectivityBroadcast(final int count) {
+ private class ExpectedBroadcast extends CompletableFuture<Intent> {
+ private final BroadcastReceiver mReceiver;
+
+ ExpectedBroadcast(BroadcastReceiver receiver) {
+ mReceiver = receiver;
+ }
+
+ public Intent expectBroadcast(int timeoutMs) throws Exception {
+ try {
+ return get(timeoutMs, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ fail("Expected broadcast not received after " + timeoutMs + " ms");
+ return null;
+ } finally {
+ mServiceContext.unregisterReceiver(mReceiver);
+ }
+ }
+
+ public Intent expectBroadcast() throws Exception {
+ return expectBroadcast(TIMEOUT_MS);
+ }
+
+ public void expectNoBroadcast(int timeoutMs) throws Exception {
+ waitForIdle();
+ try {
+ final Intent intent = get(timeoutMs, TimeUnit.MILLISECONDS);
+ fail("Unexpected broadcast: " + intent.getAction() + " " + intent.getExtras());
+ } catch (TimeoutException expected) {
+ } finally {
+ mServiceContext.unregisterReceiver(mReceiver);
+ }
+ }
+ }
+
+ /** Expects that {@code count} CONNECTIVITY_ACTION broadcasts are received. */
+ private ExpectedBroadcast registerConnectivityBroadcast(final int count) {
return registerConnectivityBroadcastThat(count, intent -> true);
}
- private ConditionVariable registerConnectivityBroadcastThat(final int count,
+ private ExpectedBroadcast registerConnectivityBroadcastThat(final int count,
@NonNull final Predicate<Intent> filter) {
- final ConditionVariable cv = new ConditionVariable();
final IntentFilter intentFilter = new IntentFilter(CONNECTIVITY_ACTION);
+ // AtomicReference allows receiver to access expected even though it is constructed later.
+ final AtomicReference<ExpectedBroadcast> expectedRef = new AtomicReference<>();
final BroadcastReceiver receiver = new BroadcastReceiver() {
- private int remaining = count;
- public void onReceive(Context context, Intent intent) {
- if (!filter.test(intent)) return;
- if (--remaining == 0) {
- cv.open();
- mServiceContext.unregisterReceiver(this);
- }
- }
- };
+ private int mRemaining = count;
+ public void onReceive(Context context, Intent intent) {
+ final int type = intent.getIntExtra(EXTRA_NETWORK_TYPE, -1);
+ final NetworkInfo ni = intent.getParcelableExtra(EXTRA_NETWORK_INFO);
+ Log.d(TAG, "Received CONNECTIVITY_ACTION type=" + type + " ni=" + ni);
+ if (!filter.test(intent)) return;
+ if (--mRemaining == 0) {
+ expectedRef.get().complete(intent);
+ }
+ }
+ };
+ final ExpectedBroadcast expected = new ExpectedBroadcast(receiver);
+ expectedRef.set(expected);
mServiceContext.registerReceiver(receiver, intentFilter);
- return cv;
+ return expected;
+ }
+
+ private ExpectedBroadcast expectConnectivityAction(int type, NetworkInfo.DetailedState state) {
+ return registerConnectivityBroadcastThat(1, intent ->
+ type == intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) && state.equals(
+ ((NetworkInfo) intent.getParcelableExtra(EXTRA_NETWORK_INFO))
+ .getDetailedState()));
}
@Test
@@ -1560,10 +1652,9 @@
// Connect the cell agent and wait for the connected broadcast.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.addCapability(NET_CAPABILITY_SUPL);
- final ConditionVariable cv1 = registerConnectivityBroadcastThat(1,
- intent -> intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE);
+ ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
mCellNetworkAgent.connect(true);
- waitFor(cv1);
+ b.expectBroadcast();
// Build legacy request for SUPL.
final NetworkCapabilities legacyCaps = new NetworkCapabilities();
@@ -1573,27 +1664,17 @@
ConnectivityManager.REQUEST_ID_UNSET, NetworkRequest.Type.REQUEST);
// File request, withdraw it and make sure no broadcast is sent
- final ConditionVariable cv2 = registerConnectivityBroadcast(1);
+ b = registerConnectivityBroadcast(1);
final TestNetworkCallback callback = new TestNetworkCallback();
mCm.requestNetwork(legacyRequest, callback);
callback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
mCm.unregisterNetworkCallback(callback);
- assertFalse(cv2.block(800)); // 800ms long enough to at least flake if this is sent
- // As the broadcast did not fire, the receiver was not unregistered. Do this now.
- mServiceContext.clearRegisteredReceivers();
+ b.expectNoBroadcast(800); // 800ms long enough to at least flake if this is sent
- // Disconnect the network and expect mobile disconnected broadcast. Use a small hack to
- // check that has been sent.
- final AtomicBoolean vanillaAction = new AtomicBoolean(false);
- final ConditionVariable cv3 = registerConnectivityBroadcastThat(1, intent -> {
- if (intent.getAction().equals(CONNECTIVITY_ACTION)) {
- vanillaAction.set(true);
- }
- return !((NetworkInfo) intent.getExtra(EXTRA_NETWORK_INFO, -1)).isConnected();
- });
+ // Disconnect the network and expect mobile disconnected broadcast.
+ b = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
mCellNetworkAgent.disconnect();
- waitFor(cv3);
- assertTrue(vanillaAction.get());
+ b.expectBroadcast();
}
@Test
@@ -1604,9 +1685,9 @@
assertNull(mCm.getActiveNetworkInfo());
assertNull(mCm.getActiveNetwork());
// Test bringing up validated cellular.
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
mCellNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
assertLength(2, mCm.getAllNetworks());
assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) ||
@@ -1614,9 +1695,9 @@
assertTrue(mCm.getAllNetworks()[0].equals(mWiFiNetworkAgent.getNetwork()) ||
mCm.getAllNetworks()[1].equals(mWiFiNetworkAgent.getNetwork()));
// Test bringing up validated WiFi.
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
assertLength(2, mCm.getAllNetworks());
assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) ||
@@ -1631,9 +1712,9 @@
assertLength(1, mCm.getAllNetworks());
assertEquals(mCm.getAllNetworks()[0], mCm.getActiveNetwork());
// Test WiFi disconnect.
- cv = registerConnectivityBroadcast(1);
+ b = registerConnectivityBroadcast(1);
mWiFiNetworkAgent.disconnect();
- waitFor(cv);
+ b.expectBroadcast();
verifyNoNetwork();
}
@@ -1641,9 +1722,9 @@
public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
// Test bringing up unvalidated WiFi
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = registerConnectivityBroadcast(1);
mWiFiNetworkAgent.connect(false);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
// Test bringing up unvalidated cellular
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
@@ -1656,19 +1737,19 @@
verifyActiveNetwork(TRANSPORT_WIFI);
// Test bringing up validated cellular
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mCellNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test cellular disconnect.
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mCellNetworkAgent.disconnect();
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
// Test WiFi disconnect.
- cv = registerConnectivityBroadcast(1);
+ b = registerConnectivityBroadcast(1);
mWiFiNetworkAgent.disconnect();
- waitFor(cv);
+ b.expectBroadcast();
verifyNoNetwork();
}
@@ -1676,25 +1757,25 @@
public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception {
// Test bringing up unvalidated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = registerConnectivityBroadcast(1);
mCellNetworkAgent.connect(false);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test bringing up unvalidated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.connect(false);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
// Test WiFi disconnect.
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.disconnect();
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test cellular disconnect.
- cv = registerConnectivityBroadcast(1);
+ b = registerConnectivityBroadcast(1);
mCellNetworkAgent.disconnect();
- waitFor(cv);
+ b.expectBroadcast();
verifyNoNetwork();
}
@@ -1702,24 +1783,24 @@
public void testUnlingeringDoesNotValidate() throws Exception {
// Test bringing up unvalidated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = registerConnectivityBroadcast(1);
mWiFiNetworkAgent.connect(false);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
// Test bringing up validated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mCellNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
// Test cellular disconnect.
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mCellNetworkAgent.disconnect();
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
// Unlingering a network should not cause it to be marked as validated.
assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
@@ -1730,25 +1811,25 @@
public void testCellularOutscoresWeakWifi() throws Exception {
// Test bringing up validated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = registerConnectivityBroadcast(1);
mCellNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test bringing up validated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
// Test WiFi getting really weak.
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.adjustScore(-11);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test WiFi restoring signal strength.
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.adjustScore(11);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
}
@@ -1766,9 +1847,9 @@
mCellNetworkAgent.expectDisconnected();
// Test bringing up validated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- final ConditionVariable cv = registerConnectivityBroadcast(1);
+ final ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
mWiFiNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
// Test bringing up unvalidated cellular.
// Expect it to be torn down because it could never be the highest scoring network
@@ -1785,33 +1866,33 @@
public void testCellularFallback() throws Exception {
// Test bringing up validated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = registerConnectivityBroadcast(1);
mCellNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test bringing up validated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
// Reevaluate WiFi (it'll instantly fail DNS).
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
assertTrue(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
mCm.reportBadNetwork(mWiFiNetworkAgent.getNetwork());
// Should quickly fall back to Cellular.
- waitFor(cv);
+ b.expectBroadcast();
assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Reevaluate cellular (it'll instantly fail DNS).
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
mCm.reportBadNetwork(mCellNetworkAgent.getNetwork());
// Should quickly fall back to WiFi.
- waitFor(cv);
+ b.expectBroadcast();
assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
@@ -1823,23 +1904,23 @@
public void testWiFiFallback() throws Exception {
// Test bringing up unvalidated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = registerConnectivityBroadcast(1);
mWiFiNetworkAgent.connect(false);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
// Test bringing up validated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mCellNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Reevaluate cellular (it'll instantly fail DNS).
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
mCm.reportBadNetwork(mCellNetworkAgent.getNetwork());
// Should quickly fall back to WiFi.
- waitFor(cv);
+ b.expectBroadcast();
assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
verifyActiveNetwork(TRANSPORT_WIFI);
@@ -1909,13 +1990,13 @@
mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
// Test unvalidated networks
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = registerConnectivityBroadcast(1);
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
cellNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- waitFor(cv);
+ b.expectBroadcast();
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
// This should not trigger spurious onAvailable() callbacks, b/21762680.
@@ -1924,28 +2005,28 @@
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- waitFor(cv);
+ b.expectBroadcast();
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.disconnect();
genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
cellNetworkCallback.assertNoCallback();
- waitFor(cv);
+ b.expectBroadcast();
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
- cv = registerConnectivityBroadcast(1);
+ b = registerConnectivityBroadcast(1);
mCellNetworkAgent.disconnect();
genericNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
- waitFor(cv);
+ b.expectBroadcast();
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
// Test validated networks
@@ -2048,10 +2129,6 @@
@Test
public void testOwnerUidCannotChange() throws Exception {
- // Owner UIDs are not visible without location permission.
- setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION);
-
final NetworkCapabilities ncTemplate = new NetworkCapabilities();
final int originalOwnerUid = Process.myUid();
ncTemplate.setOwnerUid(originalOwnerUid);
@@ -2071,6 +2148,10 @@
mWiFiNetworkAgent.setNetworkCapabilities(agentCapabilities, true);
waitForIdle();
+ // Owner UIDs are not visible without location permission.
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+
// Check that the capability change has been applied but the owner UID is not modified.
NetworkCapabilities nc = mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork());
assertEquals(originalOwnerUid, nc.getOwnerUid());
@@ -2666,9 +2747,9 @@
// Test bringing up validated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- final ConditionVariable cv = registerConnectivityBroadcast(1);
+ final ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
mWiFiNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
// Register MMS NetworkRequest
@@ -2694,9 +2775,9 @@
public void testMMSonCell() throws Exception {
// Test bringing up cellular without MMS
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
mCellNetworkAgent.connect(false);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Register MMS NetworkRequest
@@ -4304,9 +4385,9 @@
}
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
mWiFiNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
mWiFiNetworkAgent.sendLinkProperties(lp);
waitForIdle();
@@ -4862,10 +4943,10 @@
assertNotPinnedToWifi();
// Disconnect cell and wifi.
- ConditionVariable cv = registerConnectivityBroadcast(3); // cell down, wifi up, wifi down.
+ ExpectedBroadcast b = registerConnectivityBroadcast(3); // cell down, wifi up, wifi down.
mCellNetworkAgent.disconnect();
mWiFiNetworkAgent.disconnect();
- waitFor(cv);
+ b.expectBroadcast();
// Pinning takes effect even if the pinned network is the default when the pin is set...
TestNetworkPinner.pin(mServiceContext, wifiRequest);
@@ -4875,10 +4956,10 @@
assertPinnedToWifiWithWifiDefault();
// ... and is maintained even when that network is no longer the default.
- cv = registerConnectivityBroadcast(1);
+ b = registerConnectivityBroadcast(1);
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mCellNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
assertPinnedToWifiWithCellDefault();
}
@@ -4978,7 +5059,7 @@
@Test
public void testNetworkInfoOfTypeNone() throws Exception {
- ConditionVariable broadcastCV = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = registerConnectivityBroadcast(1);
verifyNoNetwork();
TestNetworkAgentWrapper wifiAware = new TestNetworkAgentWrapper(TRANSPORT_WIFI_AWARE);
@@ -5011,9 +5092,7 @@
mCm.unregisterNetworkCallback(callback);
verifyNoNetwork();
- if (broadcastCV.block(10)) {
- fail("expected no broadcast, but got CONNECTIVITY_ACTION broadcast");
- }
+ b.expectNoBroadcast(10);
}
@Test
@@ -5813,6 +5892,131 @@
mCm.unregisterNetworkCallback(callback);
}
+ private void assertGetNetworkInfoOfGetActiveNetworkIsConnected(boolean expectedConnectivity) {
+ // What Chromium used to do before https://chromium-review.googlesource.com/2605304
+ assertEquals("Unexpected result for getActiveNetworkInfo(getActiveNetwork())",
+ expectedConnectivity, mCm.getNetworkInfo(mCm.getActiveNetwork()).isConnected());
+ }
+
+ @Test
+ public void testVpnUnderlyingNetworkSuspended() throws Exception {
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(callback);
+
+ // Connect a VPN.
+ mMockVpn.establishForMyUid(false /* validated */, true /* hasInternet */,
+ false /* isStrictMode */);
+ callback.expectAvailableCallbacksUnvalidated(mMockVpn);
+
+ // Connect cellular data.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(false /* validated */);
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_CELLULAR));
+ callback.assertNoCallback();
+
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(true);
+
+ // Suspend the cellular network and expect the VPN to be suspended.
+ mCellNetworkAgent.suspend();
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_CELLULAR));
+ callback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
+ callback.assertNoCallback();
+
+ assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED);
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
+ // VPN's main underlying network is suspended, so no connectivity.
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+
+ // Switch to another network. The VPN should no longer be suspended.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false /* validated */);
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_WIFI));
+
+ // BUG: the VPN is no longer suspended, so a RESUMED callback should have been sent.
+ // callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
+ callback.assertNoCallback();
+
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED.
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ // BUG: the device has connectivity, so this should return true.
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+
+ // Unsuspend cellular and then switch back to it.
+ // The same bug happens in the opposite direction: the VPN's capabilities correctly have
+ // NOT_SUSPENDED, but the VPN's NetworkInfo is in state SUSPENDED.
+ mCellNetworkAgent.resume();
+ callback.assertNoCallback();
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_CELLULAR));
+ // Spurious double callback?
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_CELLULAR));
+ callback.assertNoCallback();
+
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED.
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ // BUG: the device has connectivity, so this should return true.
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+
+ // Re-suspending the current network fixes the problem.
+ mCellNetworkAgent.suspend();
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_CELLULAR));
+ callback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
+ callback.assertNoCallback();
+
+ assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED);
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+
+ mCellNetworkAgent.resume();
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_CELLULAR));
+ callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
+ callback.assertNoCallback();
+
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(true);
+ }
+
@Test
public void testVpnNetworkActive() throws Exception {
final int uid = Process.myUid();
@@ -6291,7 +6495,7 @@
}
@Test
- public void testVpnRestrictedUsers() throws Exception {
+ public void testRestrictedProfileAffectsVpnUidRanges() throws Exception {
// NETWORK_SETTINGS is necessary to see the UID ranges in NetworkCapabilities.
mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS,
PERMISSION_GRANTED);
@@ -6323,19 +6527,11 @@
callback.expectCapabilitiesThat(mWiFiNetworkAgent, (caps)
-> caps.hasCapability(NET_CAPABILITY_VALIDATED));
- // Create a fake restricted profile whose parent is our user ID.
- final int userId = UserHandle.getUserId(uid);
- when(mUserManager.canHaveRestrictedProfile()).thenReturn(true);
- final int restrictedUserId = userId + 1;
- final UserInfo info = new UserInfo(restrictedUserId, "user", UserInfo.FLAG_RESTRICTED);
- info.restrictedProfileParentId = userId;
- assertTrue(info.isRestricted());
- when(mUserManager.getUserInfo(restrictedUserId)).thenReturn(info);
- when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, restrictedUserId))
- .thenReturn(UserHandle.getUid(restrictedUserId, VPN_UID));
+ when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER))
+ .thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID));
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
- addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
// Send a USER_ADDED broadcast for it.
// The BroadcastReceiver for this broadcast checks that is being run on the handler thread.
@@ -6347,7 +6543,7 @@
callback.expectCapabilitiesThat(mMockVpn, (caps)
-> caps.getUids().size() == 2
&& caps.getUids().contains(new UidRange(uid, uid))
- && caps.getUids().contains(UidRange.createForUser(restrictedUserId))
+ && caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER))
&& caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_WIFI));
@@ -6357,13 +6553,13 @@
callback.expectCapabilitiesThat(mMockVpn, (caps)
-> caps.getUids().size() == 2
&& caps.getUids().contains(new UidRange(uid, uid))
- && caps.getUids().contains(UidRange.createForUser(restrictedUserId))
+ && caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER))
&& caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_WIFI));
// Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
- removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId);
+ removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
// Expect that the VPN gains the UID range for the restricted user, and that the capability
@@ -6373,53 +6569,72 @@
&& caps.getUids().contains(new UidRange(uid, uid))
&& caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_WIFI));
+ }
- // Test lockdown with restricted profiles.
+ @Test
+ public void testLockdownVpnWithRestrictedProfiles() throws Exception {
+ // For ConnectivityService#setAlwaysOnVpnPackage.
mServiceContext.setPermission(
Manifest.permission.CONTROL_ALWAYS_ON_VPN, PERMISSION_GRANTED);
+ // For call Vpn#setAlwaysOnPackage.
mServiceContext.setPermission(
Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED);
+ // Necessary to see the UID ranges in NetworkCapabilities.
mServiceContext.setPermission(
Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .build();
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, callback);
+
+ final int uid = Process.myUid();
+
// Connect wifi and check that UIDs in the main and restricted profiles have network access.
- mMockVpn.disconnect();
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true /* validated */);
- final int restrictedUid = UserHandle.getUid(restrictedUserId, 42 /* appId */);
+ final int restrictedUid = UserHandle.getUid(RESTRICTED_USER, 42 /* appId */);
assertNotNull(mCm.getActiveNetworkForUid(uid));
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
// Enable always-on VPN lockdown. The main user loses network access because no VPN is up.
final ArrayList<String> allowList = new ArrayList<>();
- mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+ mService.setAlwaysOnVpnPackage(PRIMARY_USER, ALWAYS_ON_PACKAGE, true /* lockdown */,
+ allowList);
waitForIdle();
assertNull(mCm.getActiveNetworkForUid(uid));
+ // This is arguably overspecified: a UID that is not running doesn't have an active network.
+ // But it's useful to check that non-default users do not lose network access, and to prove
+ // that the loss of connectivity below is indeed due to the restricted profile coming up.
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
// Start the restricted profile, and check that the UID within it loses network access.
- when(mUserManager.getAliveUsers()).thenReturn(
- Arrays.asList(new UserInfo[] {
- new UserInfo(userId, "", 0),
- info
- }));
+ when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER))
+ .thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID));
+ when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO,
+ RESTRICTED_USER_INFO));
// TODO: check that VPN app within restricted profile still has access, etc.
+ final Intent addedIntent = new Intent(ACTION_USER_ADDED);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
+ final Handler handler = new Handler(mCsHandlerThread.getLooper());
handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
waitForIdle();
assertNull(mCm.getActiveNetworkForUid(uid));
assertNull(mCm.getActiveNetworkForUid(restrictedUid));
// Stop the restricted profile, and check that the UID within it has network access again.
- when(mUserManager.getAliveUsers()).thenReturn(
- Arrays.asList(new UserInfo[] {
- new UserInfo(userId, "", 0),
- }));
+ when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO));
+
+ // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
+ final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
+ removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
waitForIdle();
assertNull(mCm.getActiveNetworkForUid(uid));
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
- mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+ mService.setAlwaysOnVpnPackage(PRIMARY_USER, null, false /* lockdown */, allowList);
waitForIdle();
}
@@ -6760,6 +6975,7 @@
final int userId = UserHandle.getUserId(uid);
final ArrayList<String> allowList = new ArrayList<>();
mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+ waitForIdle();
UidRangeParcel firstHalf = new UidRangeParcel(1, VPN_UID - 1);
UidRangeParcel secondHalf = new UidRangeParcel(VPN_UID + 1, 99999);
@@ -6781,10 +6997,10 @@
// Disable lockdown, expect to see the network unblocked.
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
- expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf);
callback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
vpnUidCallback.assertNoCallback();
+ expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
@@ -6827,9 +7043,11 @@
// Disable lockdown, remove our UID from the allowlist, and re-enable lockdown.
// Everything should now be blocked.
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+ waitForIdle();
expectNetworkRejectNonSecureVpn(inOrder, false, piece1, piece2, piece3);
allowList.clear();
mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+ waitForIdle();
expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf);
defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent);
@@ -6907,6 +7125,200 @@
mCm.unregisterNetworkCallback(vpnUidCallback);
}
+ private void setupLegacyLockdownVpn() {
+ final String profileName = "testVpnProfile";
+ final byte[] profileTag = profileName.getBytes(StandardCharsets.UTF_8);
+ when(mKeyStore.contains(Credentials.LOCKDOWN_VPN)).thenReturn(true);
+ when(mKeyStore.get(Credentials.LOCKDOWN_VPN)).thenReturn(profileTag);
+
+ final VpnProfile profile = new VpnProfile(profileName);
+ profile.name = "My VPN";
+ profile.server = "192.0.2.1";
+ profile.dnsServers = "8.8.8.8";
+ profile.type = VpnProfile.TYPE_IPSEC_XAUTH_PSK;
+ final byte[] encodedProfile = profile.encode();
+ when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile);
+ }
+
+ @Test
+ public void testLegacyLockdownVpn() throws Exception {
+ mServiceContext.setPermission(
+ Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED);
+
+ final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, callback);
+
+ final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(defaultCallback);
+
+ // Pretend lockdown VPN was configured.
+ setupLegacyLockdownVpn();
+
+ // LockdownVpnTracker disables the Vpn teardown code and enables lockdown.
+ // Check the VPN's state before it does so.
+ assertTrue(mMockVpn.getEnableTeardown());
+ assertFalse(mMockVpn.getLockdown());
+
+ // Send a USER_UNLOCKED broadcast so CS starts LockdownVpnTracker.
+ final int userId = UserHandle.getUserId(Process.myUid());
+ final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ final Handler handler = new Handler(mCsHandlerThread.getLooper());
+ handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
+ waitForIdle();
+
+ // Lockdown VPN disables teardown and enables lockdown.
+ assertFalse(mMockVpn.getEnableTeardown());
+ assertTrue(mMockVpn.getLockdown());
+
+ // Bring up a network.
+ // Expect nothing to happen because the network does not have an IPv4 default route: legacy
+ // VPN only supports IPv4.
+ final LinkProperties cellLp = new LinkProperties();
+ cellLp.setInterfaceName("rmnet0");
+ cellLp.addLinkAddress(new LinkAddress("2001:db8::1/64"));
+ cellLp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, "rmnet0"));
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
+ mCellNetworkAgent.connect(false /* validated */);
+ callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
+ waitForIdle();
+ assertNull(mMockVpn.getAgent());
+
+ // Add an IPv4 address. Ideally the VPN should start, but it doesn't because nothing calls
+ // LockdownVpnTracker#handleStateChangedLocked. This is a bug.
+ // TODO: consider fixing this.
+ cellLp.addLinkAddress(new LinkAddress("192.0.2.2/25"));
+ cellLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "rmnet0"));
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ callback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ waitForIdle();
+ assertNull(mMockVpn.getAgent());
+
+ // Disconnect, then try again with a network that supports IPv4 at connection time.
+ // Expect lockdown VPN to come up.
+ ExpectedBroadcast b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
+ mCellNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ b1.expectBroadcast();
+
+ // When lockdown VPN is active, the NetworkInfo state in CONNECTIVITY_ACTION is overwritten
+ // with the state of the VPN network. So expect a CONNECTING broadcast.
+ b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTING);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
+ mCellNetworkAgent.connect(false /* validated */);
+ callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
+ b1.expectBroadcast();
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED);
+
+ // TODO: it would be nice if we could simply rely on the production code here, and have
+ // LockdownVpnTracker start the VPN, have the VPN code register its NetworkAgent with
+ // ConnectivityService, etc. That would require duplicating a fair bit of code from the
+ // Vpn tests around how to mock out LegacyVpnRunner. But even if we did that, this does not
+ // work for at least two reasons:
+ // 1. In this test, calling registerNetworkAgent does not actually result in an agent being
+ // registered. This is because nothing calls onNetworkMonitorCreated, which is what
+ // actually ends up causing handleRegisterNetworkAgent to be called. Code in this test
+ // that wants to register an agent must use TestNetworkAgentWrapper.
+ // 2. Even if we exposed Vpn#agentConnect to the test, and made MockVpn#agentConnect call
+ // the TestNetworkAgentWrapper code, this would deadlock because the
+ // TestNetworkAgentWrapper code cannot be called on the handler thread since it calls
+ // waitForIdle().
+ mMockVpn.expectStartLegacyVpnRunner();
+ b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
+ ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
+ mMockVpn.establishForMyUid();
+ callback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ b1.expectBroadcast();
+ b2.expectBroadcast();
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+
+ // Switch default network from cell to wifi. Expect VPN to disconnect and reconnect.
+ final LinkProperties wifiLp = new LinkProperties();
+ wifiLp.setInterfaceName("wlan0");
+ wifiLp.addLinkAddress(new LinkAddress("192.0.2.163/25"));
+ wifiLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "wlan0"));
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
+
+ b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
+ // Wifi is CONNECTING because the VPN isn't up yet.
+ b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTING);
+ ExpectedBroadcast b3 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);
+ mWiFiNetworkAgent.connect(false /* validated */);
+ b1.expectBroadcast();
+ b2.expectBroadcast();
+ b3.expectBroadcast();
+ mMockVpn.expectStopVpnRunnerPrivileged();
+ mMockVpn.expectStartLegacyVpnRunner();
+
+ // TODO: why is wifi not blocked? Is it because when this callback is sent, the VPN is still
+ // connected, so the network is not considered blocked by the lockdown UID ranges? But the
+ // fact that a VPN is connected should only result in the VPN itself being unblocked, not
+ // any other network. Bug in isUidBlockedByVpn?
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ callback.expectCapabilitiesThat(mMockVpn, nc -> nc.hasTransport(TRANSPORT_WIFI));
+ callback.expectCallback(CallbackEntry.LOST, mMockVpn);
+ defaultCallback.expectCapabilitiesThat(mMockVpn, nc -> nc.hasTransport(TRANSPORT_WIFI));
+ defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+ defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
+
+ // While the VPN is reconnecting on the new network, everything is blocked.
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED);
+
+ // The VPN comes up again on wifi.
+ b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
+ b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
+ mMockVpn.establishForMyUid();
+ callback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ b1.expectBroadcast();
+ b2.expectBroadcast();
+
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+
+ // Disconnect cell. Nothing much happens since it's not the default network.
+ // Whenever LockdownVpnTracker is connected, it will send a connected broadcast any time any
+ // NetworkInfo is updated. This is probably a bug.
+ // TODO: consider fixing this.
+ b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
+ mCellNetworkAgent.disconnect();
+ b1.expectBroadcast();
+ callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ defaultCallback.assertNoCallback();
+
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+
+ b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ b1.expectBroadcast();
+ callback.expectCapabilitiesThat(mMockVpn, nc -> !nc.hasTransport(TRANSPORT_WIFI));
+ b2 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);
+ mMockVpn.expectStopVpnRunnerPrivileged();
+ callback.expectCallback(CallbackEntry.LOST, mMockVpn);
+ b2.expectBroadcast();
+ }
+
@Test
public final void testLoseTrusted() throws Exception {
final NetworkRequest trustedRequest = new NetworkRequest.Builder()
@@ -7250,11 +7662,11 @@
// prefix discovery is never started.
LinkProperties lp = new LinkProperties(baseLp);
lp.setNat64Prefix(pref64FromRa);
- mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp);
- mCellNetworkAgent.connect(false);
- final Network network = mCellNetworkAgent.getNetwork();
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp);
+ mWiFiNetworkAgent.connect(false);
+ final Network network = mWiFiNetworkAgent.getNetwork();
int netId = network.getNetId();
- callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
@@ -7263,8 +7675,8 @@
// If the RA prefix is withdrawn, clatd is stopped and prefix discovery is started.
lp.setNat64Prefix(null);
- mCellNetworkAgent.sendLinkProperties(lp);
- expectNat64PrefixChange(callback, mCellNetworkAgent, null);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mWiFiNetworkAgent, null);
inOrder.verify(mMockNetd).clatdStop(iface);
inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
@@ -7272,8 +7684,8 @@
// If the RA prefix appears while DNS discovery is in progress, discovery is stopped and
// clatd is started with the prefix from the RA.
lp.setNat64Prefix(pref64FromRa);
- mCellNetworkAgent.sendLinkProperties(lp);
- expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromRa);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromRa);
inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
@@ -7281,21 +7693,21 @@
// Withdraw the RA prefix so we can test the case where an RA prefix appears after DNS
// discovery has succeeded.
lp.setNat64Prefix(null);
- mCellNetworkAgent.sendLinkProperties(lp);
- expectNat64PrefixChange(callback, mCellNetworkAgent, null);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mWiFiNetworkAgent, null);
inOrder.verify(mMockNetd).clatdStop(iface);
inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
mService.mNetdEventCallback.onNat64PrefixEvent(netId, true /* added */,
pref64FromDnsStr, 96);
- expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromDns);
+ expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromDns);
inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString());
// If an RA advertises the same prefix that was discovered by DNS, nothing happens: prefix
// discovery is not stopped, and there are no callbacks.
lp.setNat64Prefix(pref64FromDns);
- mCellNetworkAgent.sendLinkProperties(lp);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
callback.assertNoCallback();
inOrder.verify(mMockNetd, never()).clatdStop(iface);
inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
@@ -7305,7 +7717,7 @@
// If the RA is later withdrawn, nothing happens again.
lp.setNat64Prefix(null);
- mCellNetworkAgent.sendLinkProperties(lp);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
callback.assertNoCallback();
inOrder.verify(mMockNetd, never()).clatdStop(iface);
inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
@@ -7315,8 +7727,8 @@
// If the RA prefix changes, clatd is restarted and prefix discovery is stopped.
lp.setNat64Prefix(pref64FromRa);
- mCellNetworkAgent.sendLinkProperties(lp);
- expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromRa);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromRa);
inOrder.verify(mMockNetd).clatdStop(iface);
inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
@@ -7330,8 +7742,8 @@
// If the RA prefix changes, clatd is restarted and prefix discovery is not started.
lp.setNat64Prefix(newPref64FromRa);
- mCellNetworkAgent.sendLinkProperties(lp);
- expectNat64PrefixChange(callback, mCellNetworkAgent, newPref64FromRa);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mWiFiNetworkAgent, newPref64FromRa);
inOrder.verify(mMockNetd).clatdStop(iface);
inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
inOrder.verify(mMockNetd).clatdStart(iface, newPref64FromRa.toString());
@@ -7341,7 +7753,7 @@
// If the RA prefix changes to the same value, nothing happens.
lp.setNat64Prefix(newPref64FromRa);
- mCellNetworkAgent.sendLinkProperties(lp);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
callback.assertNoCallback();
assertEquals(newPref64FromRa, mCm.getLinkProperties(network).getNat64Prefix());
inOrder.verify(mMockNetd, never()).clatdStop(iface);
@@ -7355,19 +7767,19 @@
// If the same prefix is learned first by DNS and then by RA, and clat is later stopped,
// (e.g., because the network disconnects) setPrefix64(netid, "") is never called.
lp.setNat64Prefix(null);
- mCellNetworkAgent.sendLinkProperties(lp);
- expectNat64PrefixChange(callback, mCellNetworkAgent, null);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mWiFiNetworkAgent, null);
inOrder.verify(mMockNetd).clatdStop(iface);
inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
mService.mNetdEventCallback.onNat64PrefixEvent(netId, true /* added */,
pref64FromDnsStr, 96);
- expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromDns);
+ expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromDns);
inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString());
inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), any());
lp.setNat64Prefix(pref64FromDns);
- mCellNetworkAgent.sendLinkProperties(lp);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
callback.assertNoCallback();
inOrder.verify(mMockNetd, never()).clatdStop(iface);
inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
@@ -7378,10 +7790,10 @@
// When tearing down a network, clat state is only updated after CALLBACK_LOST is fired, but
// before CONNECTIVITY_ACTION is sent. Wait for CONNECTIVITY_ACTION before verifying that
// clat has been stopped, or the test will be flaky.
- ConditionVariable cv = registerConnectivityBroadcast(1);
- mCellNetworkAgent.disconnect();
- callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
- waitFor(cv);
+ ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ b.expectBroadcast();
inOrder.verify(mMockNetd).clatdStop(iface);
inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
@@ -7456,10 +7868,10 @@
.destroyNetworkCache(eq(mCellNetworkAgent.getNetwork().netId));
// Disconnect wifi
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
reset(mNetworkManagementService);
mWiFiNetworkAgent.disconnect();
- waitFor(cv);
+ b.expectBroadcast();
verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
// Clean up
@@ -7581,7 +7993,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
// The uid range needs to cover the test app so the network is visible to it.
- final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
+ final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
mMockVpn.establish(lp, VPN_UID, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
@@ -7609,7 +8021,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
- final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
+ final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
@@ -7625,7 +8037,7 @@
lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun0"));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
// The uid range needs to cover the test app so the network is visible to it.
- final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
+ final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
@@ -7640,7 +8052,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
- final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
+ final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
mMockVpn.establish(lp, VPN_UID, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
@@ -7692,7 +8104,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
- final UidRange vpnRange = UidRange.createForUser(VPN_USER);
+ final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER);
final Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
mMockVpn.establish(lp, VPN_UID, vpnRanges);
assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
@@ -7757,8 +8169,22 @@
naExtraInfo.unregister();
}
+ // To avoid granting location permission bypass.
+ private void denyAllLocationPrivilegedPermissions() {
+ mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ PERMISSION_DENIED);
+ mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS,
+ PERMISSION_DENIED);
+ mServiceContext.setPermission(Manifest.permission.NETWORK_STACK,
+ PERMISSION_DENIED);
+ mServiceContext.setPermission(Manifest.permission.NETWORK_SETUP_WIZARD,
+ PERMISSION_DENIED);
+ }
+
private void setupLocationPermissions(
int targetSdk, boolean locationToggle, String op, String perm) throws Exception {
+ denyAllLocationPrivilegedPermissions();
+
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.targetSdkVersion = targetSdk;
when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
@@ -7876,7 +8302,7 @@
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
throws Exception {
- final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
+ final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
mMockVpn.setVpnType(vpnType);
@@ -8080,11 +8506,18 @@
assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
}
+ public NetworkAgentInfo fakeMobileNai(NetworkCapabilities nc) {
+ final NetworkInfo info = new NetworkInfo(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_LTE,
+ ConnectivityManager.getNetworkTypeName(TYPE_MOBILE),
+ TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE));
+ return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(),
+ nc, 0, mServiceContext, null, new NetworkAgentConfig(), mService, null, null, null,
+ 0, INVALID_UID);
+ }
+
@Test
public void testCheckConnectivityDiagnosticsPermissionsNetworkStack() throws Exception {
- final NetworkAgentInfo naiWithoutUid =
- new NetworkAgentInfo(null, null, null, null, new NetworkCapabilities(), 0,
- mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
+ final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities());
mServiceContext.setPermission(
android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
@@ -8097,9 +8530,7 @@
@Test
public void testCheckConnectivityDiagnosticsPermissionsWrongUidPackageName() throws Exception {
- final NetworkAgentInfo naiWithoutUid =
- new NetworkAgentInfo(null, null, null, null, new NetworkCapabilities(), 0,
- mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
+ final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities());
mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
@@ -8112,9 +8543,7 @@
@Test
public void testCheckConnectivityDiagnosticsPermissionsNoLocationPermission() throws Exception {
- final NetworkAgentInfo naiWithoutUid =
- new NetworkAgentInfo(null, null, null, null, new NetworkCapabilities(), 0,
- mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
+ final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities());
mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
@@ -8127,22 +8556,17 @@
@Test
public void testCheckConnectivityDiagnosticsPermissionsActiveVpn() throws Exception {
- final Network network = new Network(NET_ID);
- final NetworkAgentInfo naiWithoutUid =
- new NetworkAgentInfo(null, network, null, null, new NetworkCapabilities(), 0,
- mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
-
- setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION);
+ final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities());
mMockVpn.establishForMyUid();
assertUidRangesUpdatedForMyUid(true);
// Wait for networks to connect and broadcasts to be sent before removing permissions.
waitForIdle();
- mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
- assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {network}));
+ assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {naiWithoutUid.network}));
waitForIdle();
assertTrue(
"Active VPN permission not applied",
@@ -8163,9 +8587,7 @@
public void testCheckConnectivityDiagnosticsPermissionsNetworkAdministrator() throws Exception {
final NetworkCapabilities nc = new NetworkCapabilities();
nc.setAdministratorUids(new int[] {Process.myUid()});
- final NetworkAgentInfo naiWithUid =
- new NetworkAgentInfo(null, null, null, null, nc, 0, mServiceContext, null, null,
- mService, null, null, null, 0, INVALID_UID);
+ final NetworkAgentInfo naiWithUid = fakeMobileNai(nc);
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
@@ -8182,9 +8604,7 @@
final NetworkCapabilities nc = new NetworkCapabilities();
nc.setOwnerUid(Process.myUid());
nc.setAdministratorUids(new int[] {Process.myUid()});
- final NetworkAgentInfo naiWithUid =
- new NetworkAgentInfo(null, null, null, null, nc, 0, mServiceContext, null, null,
- mService, null, null, null, 0, INVALID_UID);
+ final NetworkAgentInfo naiWithUid = fakeMobileNai(nc);
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
@@ -8405,6 +8825,7 @@
mCm.registerNetworkCallback(genericRequest, genericNetworkCallback);
mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+ waitForIdle();
final ConnectivityService.NetworkRequestInfo[] nriOutput = mService.requestsSortedById();
@@ -8450,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);
@@ -8466,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/VcnTransportInfoTest.java b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
new file mode 100644
index 0000000..3156190
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn;
+
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+
+import android.net.wifi.WifiInfo;
+import android.os.Parcel;
+
+import org.junit.Test;
+
+public class VcnTransportInfoTest {
+ private static final int SUB_ID = 1;
+ private static final int NETWORK_ID = 5;
+ private static final WifiInfo WIFI_INFO =
+ new WifiInfo.Builder().setNetworkId(NETWORK_ID).build();
+
+ private static final VcnTransportInfo CELL_UNDERLYING_INFO = new VcnTransportInfo(SUB_ID);
+ private static final VcnTransportInfo WIFI_UNDERLYING_INFO = new VcnTransportInfo(WIFI_INFO);
+
+ @Test
+ public void testGetWifiInfo() {
+ assertEquals(WIFI_INFO, WIFI_UNDERLYING_INFO.getWifiInfo());
+
+ assertNull(CELL_UNDERLYING_INFO.getWifiInfo());
+ }
+
+ @Test
+ public void testGetSubId() {
+ assertEquals(SUB_ID, CELL_UNDERLYING_INFO.getSubId());
+
+ assertEquals(INVALID_SUBSCRIPTION_ID, WIFI_UNDERLYING_INFO.getSubId());
+ }
+
+ @Test
+ public void testEquals() {
+ assertEquals(CELL_UNDERLYING_INFO, CELL_UNDERLYING_INFO);
+ assertEquals(WIFI_UNDERLYING_INFO, WIFI_UNDERLYING_INFO);
+ assertNotEquals(CELL_UNDERLYING_INFO, WIFI_UNDERLYING_INFO);
+ }
+
+ @Test
+ public void testParcelUnparcel() {
+ verifyParcelingIsNull(CELL_UNDERLYING_INFO);
+ verifyParcelingIsNull(WIFI_UNDERLYING_INFO);
+ }
+
+ private void verifyParcelingIsNull(VcnTransportInfo vcnTransportInfo) {
+ Parcel parcel = Parcel.obtain();
+ vcnTransportInfo.writeToParcel(parcel, 0 /* flags */);
+ assertNull(VcnTransportInfo.CREATOR.createFromParcel(parcel));
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java
new file mode 100644
index 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);
+ }
+}